C# 如何并行处理潜在的异步聚合任务,并在每个任务统一完成时对其进行处理
我陷入了一种情况,在“正常”的情况下,c#land可能会相对容易解决,但由于需要注意统一性,这就有点复杂了 最重要的是我有一组需要运行的任务,其中一些可以并行运行,一些是异步的,其他的不是。对于那些可以并行运行的任务,当每个任务完成时,我需要触发一个事件(在主线程中),让任何相关方知道任务已经完成 为了深入了解细节,这是针对Unity中的一个游戏,但是这个特定的代码要求它不能接触Unity API,因为它将在一个纯c#的单独项目中 从概念上讲,在我的游戏中,我有一个“日转换”。每晚玩家在睡觉时(或是用尽筋疲力尽或被敌人击倒),游戏将进入加载屏幕,处理更长的运行操作,我不想在游戏中间进行。例如运行模拟以更新日常经济状况,或将文件I/O加载到第二天要使用的内容中 这些take可能是线程安全的,也可能不是真正的异步调用(例如,文件加载) 我的目标是以一种尽可能少地阻塞主线程的方式来实现这一点(一些阻塞将会发生,这是不可避免的,因为一些任务可能会触及需要在主线程上发生的Unity API。这些不是线程安全的) 因为有些事情是异步的,所以我把所有事情都当作是可以等待的 My DayTransitioner类有一个IDayTransitionAction对象列表:C# 如何并行处理潜在的异步聚合任务,并在每个任务统一完成时对其进行处理,c#,unity3d,parallel-processing,async-await,C#,Unity3d,Parallel Processing,Async Await,我陷入了一种情况,在“正常”的情况下,c#land可能会相对容易解决,但由于需要注意统一性,这就有点复杂了 最重要的是我有一组需要运行的任务,其中一些可以并行运行,一些是异步的,其他的不是。对于那些可以并行运行的任务,当每个任务完成时,我需要触发一个事件(在主线程中),让任何相关方知道任务已经完成 为了深入了解细节,这是针对Unity中的一个游戏,但是这个特定的代码要求它不能接触Unity API,因为它将在一个纯c#的单独项目中 从概念上讲,在我的游戏中,我有一个“日转换”。每晚玩家在睡觉时(
public interface IDayTransitionAction
{
bool IsRepeating { get; }
bool IsThreadSafe { get; }
Task ProcessTransitionAsync();
}
这里确实需要两个截然不同的过程
第一个是线程安全的代码(异步或非异步)需要能够以实际并行运行的方式启动,并且当它们完成时,需要在主线程上触发一个事件
protected async Task ProcessThreadSafeTasks()
{
//1st kick off in parallel, probably using one of the Task
//methods such as WhenAll or WhenAny or some other voodoo...
//Not really sure what API calls or structure to use to do it...
//As each comes back an event needs to be fired.
//The event needs to occur on the main thread.
//this.OnIndividualItemProcessed();
}
第二,非线程安全的东西需要运行并等待(我知道这个组中的同步进程将阻塞主线程,但如果一个进程是真正异步的,它就不应该阻塞主线程)
第二种情况实际上是最容易解决的,因为Unity提供了一个同步上下文,强制在主线程上执行。因此,我认为后者可以在一个循环中等待
protected async Task ProcessNonThreadSafeTasks()
{
foreach (var actionItem in this.nonThreadSafeActions)
{
await actionItem.ProcessTransitionAsync();
//Raise the OnIndividualItemProcessed event so eventually
//a loading bar can be filled in as tasks complete, though
//this class doesn't know or care about the loading bar
//specifically. Just that interested parties want to know
//when an individual task is completed
this.OnIndividualItemProcessed();
}
}
这是第一个让我头疼的令人讨厌的案例,因为我真的不知道如何启动它们,使它们能够并行运行,同时在每个单独的事件完成时发送事件(特别是考虑到Unity的同步上下文…我想我必须暂时以某种方式覆盖它)。我将如何着手实现这一点
提前谢谢
另外,为了便于参考,将调用上述两个函数的方法
public async Task ProcessTransitionActions()
{
//Used primarily to lock down the lists while processing is ongoing.
//The Add method will dump anything that's attempting to be stored
//to another list to be sorted and added to the main lists after processing
//is finished.
IsProcessing = true;
await this.ProcessThreadSafeTasks();
await this.ProcessNonThreadSafeTasks();
//Purge any actions that aren't repeating.
this.CleanNonRepeatingActions();
//Add any actions that were stored while processing to the main lists.
this.SortStoredActions();
IsProcessing = false;
//Send out an event to allow interested parties to know processing is
//finished.
this.OnProcessingCompleted();
}
编辑1*
只是为了澄清,“线程安全”操作旨在在其他线程(真正的并行)上运行(或至少能够运行),而不是简单地在主线程上并发运行。当它们完成时,我需要拍摄事件,该事件特别需要在主线程上播放。您可能需要这样的内容
protected async Task ProcessThreadSafeTasks()
{
//1st kick off in parallel, probably using one of the Task
//methods such as WhenAll or WhenAny or some other voodoo...
//Not really sure what API calls or structure to use to do it...
//As each comes back an event needs to be fired.
//The event needs to occur on the main thread.
//this.OnIndividualItemProcessed();
}
如果你有几千个,效率不是特别高,但是如果你有几十个,效果应该很好
class TransitionRunner
{
readonly List<IDayTransitionAction> list = new List<IDayTransitionAction>();
readonly HashSet<Task> runningTasks = new HashSet<Task>();
public async Task runAll()
{
Debug.Assert( 0 == runningTasks.Count );
foreach( var dta in list )
{
if( dta.IsThreadSafe )
{
// Thread safe tasks are started and finished on the thread pool's threads
Func<Task> fnRun = dta.ProcessTransitionAsync;
runningTasks.Add( Task.Run( fnRun ) );
}
else
{
// Non-thread safe tasks are started and finished on the GUI thread.
// If they're not actually async, the following line will run the complete task.
Task task = dta.ProcessTransitionAsync();
if( task.IsCompleted )
{
// It was a blocking task without await
// Or maybe await that completed extremely fast.
if( task.IsCompletedSuccessfully )
OnIndividualItemProcessed();
else
await task; // Propagate exception
}
else
runningTasks.Add( task );
}
}
while( runningTasks.Count > 0 )
{
runningTasks.Remove( await Task.WhenAny( runningTasks ) );
OnIndividualItemProcessed();
}
}
}
类转换运行程序
{
只读列表=新列表();
readonly HashSet runningTasks=new HashSet();
公共异步任务runAll()
{
Assert(0==runningTasks.Count);
foreach(列表中的var dta)
{
if(dta.IsThreadSafe)
{
//线程安全任务在线程池的线程上启动和完成
Func fnRun=dta.ProcessTransitionAsync;
runningTasks.Add(Task.Run(fnRun));
}
其他的
{
//非线程安全任务在GUI线程上启动和完成。
//如果它们实际上不是异步的,那么下面一行将运行完整的任务。
Task Task=dta.ProcessTransitionAsync();
如果(task.IsCompleted)
{
//这是一项迫不及待的阻拦任务
//或者,也许等待它以极快的速度完成。
如果(task.IsCompletedSuccessfully)
onIndividualTemprocessed();
其他的
等待任务;//传播异常
}
其他的
runningTasks.Add(任务);
}
}
while(runningTasks.Count>0)
{
runningTasks.Remove(wait Task.WhenAny(runningTasks));
onIndividualTemprocessed();
}
}
}
您可能需要这样的东西
如果你有几千个,效率不是特别高,但是如果你有几十个,效果应该很好
class TransitionRunner
{
readonly List<IDayTransitionAction> list = new List<IDayTransitionAction>();
readonly HashSet<Task> runningTasks = new HashSet<Task>();
public async Task runAll()
{
Debug.Assert( 0 == runningTasks.Count );
foreach( var dta in list )
{
if( dta.IsThreadSafe )
{
// Thread safe tasks are started and finished on the thread pool's threads
Func<Task> fnRun = dta.ProcessTransitionAsync;
runningTasks.Add( Task.Run( fnRun ) );
}
else
{
// Non-thread safe tasks are started and finished on the GUI thread.
// If they're not actually async, the following line will run the complete task.
Task task = dta.ProcessTransitionAsync();
if( task.IsCompleted )
{
// It was a blocking task without await
// Or maybe await that completed extremely fast.
if( task.IsCompletedSuccessfully )
OnIndividualItemProcessed();
else
await task; // Propagate exception
}
else
runningTasks.Add( task );
}
}
while( runningTasks.Count > 0 )
{
runningTasks.Remove( await Task.WhenAny( runningTasks ) );
OnIndividualItemProcessed();
}
}
}
类转换运行程序
{
只读列表=新列表();
readonly HashSet runningTasks=new HashSet();
公共异步任务runAll()
{
Assert(0==runningTasks.Count);
foreach(列表中的var dta)
{
if(dta.IsThreadSafe)
{
//线程安全任务在线程池的线程上启动和完成
Func fnRun=dta.ProcessTransitionAsync;
runningTasks.Add(Task.Run(fnRun));
}
其他的
{
//非线程安全任务在GUI线程上启动和完成。
//如果它们实际上不是异步的,那么下面一行将运行完整的任务。
Task Task=dta.ProcessTransitionAsync();
如果(task.IsCompleted)
{
//这是一项迫不及待的阻拦任务
//或者等着那个com
private async Task ProcessActionAndRaiseEvent(ActionItem actionItem)
{
await Task.Run(() => actionItem.ProcessTransitionAsync());
this.OnIndividualItemProcessed();
}