我的函数将很快返回信息。这真是个好消息!但是,由于并行循环正在异步运行,因此该函数会在循环完成之前返回一个值,除非我在主线程上执行了一些长时间运行的任务以等待结果。没有用户界面,所以我正在使用async / await尝试在TPL中快速获得结果。
我引入了一个布尔标志值和while循环以等待结果。
这可行,但看起来很奇怪。
在我的特定情况下,是否有更好的方法来“等待”结果。 当我使用“ while循环”时,这似乎是第一个代码片段。
注意:我应该提到,因为这是一个Alexa响应,所以在此功能之外是一个任务,“ Task.Delays”持续八秒钟,然后返回响应,以防我的其他任务花很长时间并且Alexa打算超时。
private static string SavedImageAnalysisResult(IReadOnlyCollection<Image> savedImageList, ConfigurationDto config)
{
string result = "[]";
BreakImageAnalysis = false;
if (!savedImageList.Any()) return result;
Parallel.ForEach(savedImageList, new ParallelOptions
{
MaxDegreeOfParallelism = 5000
},
async (image, loopState) =>
{
Task.Run(() => Console.Write("██"));
string threadLocalAnalysisResult =
await AnalyzeImageAsync(image.ImageBytes, config);
if (IsEmptyOrErrorAnalysis(threadLocalAnalysisResult)) return;
Task.Run(() => Console.Write("█ █"));
result = threadLocalAnalysisResult;
BreakImageAnalysis = true;
loopState.Break();
});
while (!BreakImageAnalysis) if (BreakImageAnalysis) break; //strange to do this?
return result;
}
此函数的调用方式如下:
public static List<Person> DetectPersonAsync()
{
Task.Run(() => Console.WriteLine("{0}\nNew Person Detection Requested...", DateTime.Now.ToString("f")));
ConfigurationDto config = Configuration.GetSettings();
camera = new SecurityCamera();
byte[] imageData = camera.GetImageAsByte(config.SecurityCameraUrl +
config.SecurityCameraStaticImage +
DateTime.Now);
if (!imageData.Any()) return null;
string imageAnalysis = "[]";
SavedImageList = camera.ImageCache;
Task.Run(() => Console.WriteLine("\nBegin Image Analysis...\n"));
var imageAnalysisTasks = new[]
{
Task.Factory.StartNew(() => SavedImageAnalysisResult(SavedImageList, config)),
Task.Factory.StartNew(() => InProgressImageAnalysisResult(camera, config))
};
Task.WaitAll(imageAnalysisTasks);
Task.Run(() => Console.WriteLine("\n\nAnalysis complete\n"));
if (!IsEmptyOrErrorAnalysis(imageAnalysisTasks[0].Result))
imageAnalysis = imageAnalysisTasks[0].Result;
if (!IsEmptyOrErrorAnalysis(imageAnalysisTasks[1].Result))
imageAnalysis = imageAnalysisTasks[1].Result;
return !IsEmptyOrErrorAnalysis(imageAnalysis)
? JsonConvert.DeserializeObject<List<Person>>(imageAnalysis)
: new List<Person>();
}
但是此函数的调用方式如下:
if (alexa.IsLaunchRequest(alexaRequest))
{
//We don't want to wait for these two tasks to return
Task.Run(() => SendSecurityImageToMagicMirrorUi());
Task.Run(() => alexa.PostDirectiveResponseAsync(alexaRequest));
//On your marks get set go! 8 seconds and counting
return await Task.WhenAny(new[]
{
Task.Run(() => GetAlexaCognitiveResponseAsync()),
Task.Run(() => AlexaRequestTimeoutMonitor())
}).Result;
}
最后还有超时功能,如果8秒钟结束,它将返回:
private static async Task<object> AlexaRequestTimeoutMonitor()
{
await Task.Delay(new TimeSpan(0, 0, 0, 8));
return AlexaApi.ResponseBuilder(CreateNoPersonDetectionPhrase(new AlexaSynthesisResponseLibrary()), false);
}
它在'CreateNoPersonDetectedPhrase'函数中,在其中我将'IsFound'布尔标志改回'false'
答案 0 :(得分:1)
您的Parallel.ForEach
启动异步委托,实际上它们正在并行执行,直到await AnalyzeImageAsync
点返回Task
作为承诺。现在有了这个承诺Parallel.ForEach
“认为”此任务已完成,而实际上异步操作很可能才刚刚开始。但是没有人在等待,Parallel.ForEach
很快就完成了。因此,您需要在Parallel.ForEach
循环之外公开这些承诺,以便例如可以使用Task.WhenAll
等待它们。
但是我也建议考虑是否完全需要这种并行化,因为如果大多数AnalyzeImageAsync
操作(此处未介绍)不是CPU限制的,则仅使用异步性更为合理
等待Parallels.Forxxx
中的异步任务的最简单方法(我并不是说最好的)
public static async Task SomeAsyncTask()
{
await Task.Delay(5000);
Console.WriteLine("2222");
}
public static async Task Loop()
{
var collection = new[] { 1, 2, 3, 4 };
var asyncTasks = new Task[4];
Parallel.ForEach(collection,
(item, loop, index) =>
{
Console.WriteLine("1111");
asyncTasks[index] = SomeAsyncTask();
});
await Task.WhenAll(asyncTasks);
}
请注意,SomeAsyncTask()
之后的所有内容都应移到任务的继续中(在本例中为Console.WriteLine("2222")
)。