【发布时间】:2022-11-11 21:18:57
【问题描述】:
我为学习目的编写了自己的 JobScheduler。这个想法非常简单,它启动 n 个线程,从并发队列中提取作业/任务,处理它们,一旦完成,它将通知一个事件,以便主线程可以等待它完成(如果他愿意的话)。
线程循环看起来像这样......
internal long ItemCount; // The amount of jobs to process
internal ManualResetEventSlim Event { get; set; } // Event to notify worker threads for new items
internal ConcurrentQueue<JobMeta> Jobs { get; set; } // Jobs
private void Loop(CancellationToken token) {
Loop:
// Break if cancellation is requested
if (token.IsCancellationRequested) return;
// Make threads wait, the event tells them when new jobs arrived
Event.Wait(token);
if (Jobs.TryDequeue(out var jobMeta)) { // Concurrent, dequeue one at a time
// Make other threads wait once no more items are in the queue
if(Interlocked.Decrement(ref ItemCount) == 0) Event.Reset();
jobMeta.Job.Execute(); // Execute job
jobMeta.JobHandle.Set(); // ManualResetEvent.Set to notify the main e.g.
}
goto Loop;
}
// Notify threads about new arrived jobs
public void NotifyThreads() {
Interlocked.Exchange(ref ItemCount, Jobs.Count); // Set ItemCount
Event.Set(); // Notify
}
// Enqueues new job
public JobHandle Schedule(IJob job) {
var handle = new ManualResetEvent(false);
var jobMeta = new JobMeta{ JobHandle = handle, Job = job};
Jobs.Enqueue(jobMeta);
return handle;
}
但是,如果我执行以下操作,有时这会导致死锁:
var jobHandle = threadScheduler.Schedule(myJob); // JobHandle is a ManualResetEvent
threadScheduler.NotifyThreads();
for(var index = 0; index < 10000; index++){
var otherJobHandle = threadScheduler.Schedule(otherJob);
threadScheduler.NotifyThreads();
otherJobHandle.Wait();
}
jobHandle.Wait(); // Deadlock sometimes...
为什么这会导致死锁?逻辑问题在哪里?一个普通的 JobScheduler 会是什么样子(因为我一般找不到关于这个主题的任何好的信息)?
很高兴有任何帮助!
【问题讨论】:
-
使用
BlockingCollection<T>类可以大大简化您尝试做的事情,而不是使用低级别的ManualResetEventSlims 和ConcurrentQueue<T>s。 -
@TheodorZoulias 谢谢!但是我也遇到了阻塞集合的死锁......所以我想那部分不是问题:/
-
很难说问题出在哪里,因为您没有提供 MVCE ... fx
Schedule返回JobHandle(public JobHandle Schedule(IJob job)但您返回ManualResetEvent... 谁知道还有哪些其他代码丢失/更改这里 -
您是否尝试过用单个
BlockingCollection<JobMeta>替换所有三个状态字段(long ItemCount、ManualResetEventSlim Event和ConcurrentQueue<JobMeta> Jobs),但您仍然遇到死锁? -
@TheodorZoulias 正是......而且它变得越来越奇怪。只有当我将它作为发行版运行时...调试我当前的代码和带有blockingcollection的修改版本才能完美地工作。
标签: c# multithreading asynchronous deadlock