细节:
我有一个游戏,有两个独立的AI在对抗。 每个AI都有自己的任务。 这两个任务需要同时开始,并且需要取一些参数并返回值。 现在我想并行运行100-200场比赛(每场比赛中有两个任务)。
问题是两个任务并不是同时开始。它们在所有可用资源空闲时才会随机开始。
代码:
我的当前方法如下所示。
- 我有一个包含一些参数的inputobjects列表。
- 使用Parallel.ForEach为每个inputobject创建一个游戏和两个AI。
- 无论哪个AI先完成游戏,都会使用CancellationToken停止另一个AI,使其玩相同的游戏。
- 所有返回的值都保存在ConcurrentBag中。
因为只有这样,每个游戏的两个AI任务并不会同时启动,所以我添加了一个AutoResetEvent。我希望我可以等待一个任务,直到第二个任务已经开始,但是AutoResetEvent.WaitOne却阻塞了所有资源。因此,使用AutoResetEvent的结果是第一个AI任务开始并等待第二个任务开始,但由于它们没有释放线程,它们将永远等待。
private ConcurrentBag<Individual> TrainKis(List<Individual> population) {
ConcurrentBag<Individual> resultCollection = new ConcurrentBag<Individual>();
ConcurrentBag<Individual> referenceCollection = new ConcurrentBag<Individual>();
Parallel.ForEach(population, individual =>
{
GameManager gm = new GameManager();
CancellationTokenSource c = new CancellationTokenSource();
CancellationToken token = c.Token;
AutoResetEvent waitHandle = new AutoResetEvent(false);
KI_base eaKI = new KI_Stupid(gm, individual.number, "KI-" + individual.number, Color.FromArgb(255, 255, 255));
KI_base referenceKI = new KI_Stupid(gm, 999, "REF-" + individual.number, Color.FromArgb(0, 0, 0));
Individual referenceIndividual = CreateIndividual(individual.number, 400, 2000);
var t1 = referenceKI.Start(token, waitHandle, referenceIndividual).ContinueWith(taskInfo => {
c.Cancel();
return taskInfo.Result;
}).Result;
var t2 = eaKI.Start(token, waitHandle, individual).ContinueWith(taskInfo => {
c.Cancel();
return taskInfo.Result;
}).Result;
referenceCollection.Add(t1);
resultCollection.Add(t2);
});
return resultCollection;
}
这是 AI 的开始方法,我在这里等待第二个 AI 的下棋:
public Task<Individual> Start(CancellationToken _ct, AutoResetEvent _are, Individual _i) {
i = _i;
gm.game.kis.Add(this);
if (gm.game.kis.Count > 1) {
_are.Set();
return Task.Run(() => Play(_ct));
}
else {
_are.WaitOne();
return Task.Run(() => Play(_ct));
}
}
还有简化的玩法方法
public override Individual Play(CancellationToken ct) {
Console.WriteLine($"{player.username} started.");
while (Constants.TOWN_NUMBER*0.8 > player.towns.Count || player.towns.Count == 0) {
try {
Thread.Sleep((int)(Constants.TOWN_GROTH_SECONDS * 1000 + 10));
}
catch (Exception _ex) {
Console.WriteLine($"{player.username} error: {_ex}");
}
//here are the actions of the AI (I removed them for better overview)
if (ct.IsCancellationRequested) {
return i;
}
}
if (Constants.TOWN_NUMBER * 0.8 <= player.towns.Count) {
winner = true;
return i;
}
return i;
}
有没有更好的方法来做到这一点,保持所有事物不变,但确保每个游戏中的两个KI任务同时开始?
lock
或其他同步原语进行同步,还是无锁的? - Theodor ZouliasRandom.Next()
。顺便说一句,“完全自由”包括可能一个玩家根本没有行动的可能性,这正是您目前正在经历的。 :-) - Theodor Zoulias