C# 并行执行多对并发任务
详情: 我有一个游戏,两个独立的人工智能相互对抗。 每个AI都有自己的任务。 这两个任务需要同时启动,需要获取一些参数并返回一个值。 现在我想并行运行100-200个游戏(每两个任务) 我现在遇到的问题是,这两项任务不是同时开始的。只要有空闲资源,它们就完全随机启动 代码: 我目前的做法如下C# 并行执行多对并发任务,c#,.net,multithreading,task,C#,.net,Multithreading,Task,详情: 我有一个游戏,两个独立的人工智能相互对抗。 每个AI都有自己的任务。 这两个任务需要同时启动,需要获取一些参数并返回一个值。 现在我想并行运行100-200个游戏(每两个任务) 我现在遇到的问题是,这两项任务不是同时开始的。只要有空闲资源,它们就完全随机启动 代码: 我目前的做法如下 我有一个inputObject列表,其中包括一些参数 使用Parallel.ForEach,我为每个inputobject创建一个游戏和两个游戏AI 无论哪个AI先完成游戏,都会使用Cancellatio
- 我有一个inputObject列表,其中包括一些参数
- 使用Parallel.ForEach,我为每个inputobject创建一个游戏和两个游戏AI
- 无论哪个AI先完成游戏,都会使用CancellationToken停止另一个AI玩同一游戏
- 所有返回值都保存在ConcurrentBag中
private ConcurrentBag TrainKis(列表人群){
ConcurrentBag resultCollection=新ConcurrentBag();
ConcurrentBag referenceCollection=新ConcurrentBag();
Parallel.ForEach(总体、个人=>
{
GameManager gm=新GameManager();
CancellationTokenSource c=新的CancellationTokenSource();
CancellationToken=c.令牌;
AutoResetEvent waitHandle=新的AutoResetEvent(假);
KI_base eaKI=新KI_dup(gm,individual.number,“KI-”+individual.number,Color.FromArgb(255,255,255));
KI_base referenceKI=新的KI_dup(gm,999,“REF-”+individual.number,Color.FromArgb(0,0,0));
Individual referenceIndividual=CreateIndividual(Individual.number,4002000);
var t1=referenceKI.Start(令牌、waitHandle、referenceIndividual)。ContinueWith(taskInfo=>{
c、 取消();
返回taskInfo.Result;
}).结果;
var t2=eaKI.Start(令牌、waitHandle、个人).ContinueWith(taskInfo=>{
c、 取消();
返回taskInfo.Result;
}).结果;
referenceCollection.Add(t1);
结果收集。添加(t2);
});
返回结果集合;
}
这是AI的启动方法,我在这里等待第二个AI的播放:
公共任务启动(取消令牌、自动恢复事件、个人){
i=_i;
gm.game.kis.Add(这个);
如果(gm.game.kis.Count>1){
_are.Set();
返回任务。运行(()=>Play(_-ct));
}
否则{
_是。WaitOne();
返回任务。运行(()=>Play(_-ct));
}
}
以及简化的播放方法
公共覆盖单个播放(取消令牌ct){
Console.WriteLine($“{player.username}已启动。”);
while(常数.TOWN_NUMBER*0.8>player.towns.Count | | player.towns.Count==0){
试一试{
线程睡眠((int)(Constants.TOWN_GROTH_SECONDS*1000+10));
}
捕获(例外情况){
Console.WriteLine($“{player.username}错误:{u ex}”);
}
//以下是AI的操作(为了更好地概述,我删除了它们)
如果(ct.IsCancellationRequested){
返回i;
}
}
如果(Constants.TOWN_NUMBER*0.8我的建议是更改Play
方法的签名,使其返回一个任务
,而不是个人
,并将对线程.Sleep
的调用替换为等待任务.Delay
。这个小小的更改应该会对响应产生显著的积极影响在AI玩家中,因为没有线程会被他们阻止,而ThreadPool
线程的小池将得到最佳利用
public override async Task<Individual> Play(CancellationToken ct)
{
Console.WriteLine($"{player.username} started.");
while (Constants.TOWN_NUMBER * 0.8 > player.towns.Count || player.towns.Count == 0)
{
//...
await Task.Delay((int)(Constants.TOWN_GROTH_SECONDS * 1000 + 10));
//...
}
}
这样,您将获得最大的并发性,这可能并不理想,因为您机器的CPU或RAM或CPU RAM带宽可能会饱和。您可以寻找限制并发异步操作数量的方法。两个AI播放器如何相互交互?它们是否读写到某个共享状态?是吗这些读/写操作使用lock
或其他同步原语进行同步,或者它们是无锁的?两个AI都与同一个游戏管理器交互。某些部分(如包含游戏当前状态的四叉树)为防止错误,游戏管理器的密码被锁定。是否可以使用任务
s来协调每对AI玩家?其思想是将每一位AI玩家公开为一个(返回IEnumerable
并包含yield
语句的方法)而不是任务
,并为每个“展开”的游戏指定一个任务
两个迭代器一步一个脚印。然后两个玩家总是轮流移动吗?那么KI1做一个动作,然后KI2,KI1…等等?两个KI都需要完全自由地玩…哦,我明白了。你能在你的问题中包括一个AI玩家异步Start
方法的简化示例吗?这样我们就可以提供一个替代建议?(而不是协同程序)@theoretisch简言之,找到所有.Wait()
和.ResultTask[] tasks = population.Select(async individual =>
{
GameManager gm = new GameManager();
CancellationTokenSource c = new CancellationTokenSource();
//...
var task1 = referenceKI.Start(token, waitHandle, referenceIndividual);
var task2 = eaKI.Start(token, waitHandle, individual);
await Task.WhenAny(task1, task2);
c.Cancel();
await Task.WhenAll(task1, task2);
var result1 = await task1;
var result2 = await task2;
referenceCollection.Add(result1);
resultCollection.Add(result2);
}).ToArray();
Task.WaitAll(tasks);