多个任务中的一个在互斥体中获取锁定的时间比其他任务长得多
本文关键字:任务 时间 锁定 获取 其他 一个 | 更新日期: 2023-09-27 18:34:46
情况
目前在我的项目中,我有 3 个内部有一个工作循环的Workers
,还有一个CommonWork
类对象,其中包含Workers
可以调用的Work
方法(DoFirstTask、DoSecondTask、DoThirdTask(。每个Work
方法必须相对于其他方法相互独占执行。每个方法都会生成更多的嵌套Tasks
,这些嵌套等待它们完成。
问题
当所有 3 Workers
都启动时,2 Workers
以相同的速度执行,但第 3 Worker
滞后或第 1 Worker
超快,第 2 慢一点,第 3 非常慢,这取决于现实世界。
离奇
当只有 2 Workers
在工作时,它们也可以很好地分担工作,并以相同的速度执行。
更有趣的是,即使是第 3 Worker
调用的CommonWork
方法数量也更少,并且有可能执行更多的循环循环,但它没有。我试图在下面的代码中模拟条件:
if (Task.CurrentId.Value < 3)
调试时,我发现第 3 Worker
正在等待获取比其他Workers
长得多的Mutex
上的锁。有时,其他两个Workers
只是互换工作,而第三个一直在等待Mutex.WaitOne()
;我想,没有真正进入它,因为其他Workers
在获得那把锁方面没有问题!
我已经尝试过了
我尝试以TaskCreateOptions.LongRunning
Worker
Tasks
启动,但没有任何变化。我还尝试通过指定TaskCreateOpions.AttachedToParent
使嵌套Tasks
成为子Tasks
,认为它可能与本地队列和调度有关,但显然不是。
简化代码
下面是我的实际应用程序的简化代码。可悲的是,我无法在这个简单的例子中重现这种情况:
class Program
{
public class CommonWork
{
private Mutex _mutex;
public CommonWork() { this._mutex = new Mutex(false); }
private void Lock() { this._mutex.WaitOne(); }
private void Unlock() { this._mutex.ReleaseMutex(); }
public void DoFirstTask(int taskId)
{
this.Lock();
try
{
// imitating sync work from 3rd Party lib, that I need to make async
var t = Task.Run(() => {
Thread.Sleep(500); // sync work
});
... // doing some work here
t.Wait();
Console.WriteLine("Task {0}: DoFirstTask - complete", taskId);
}
finally { this.Unlock(); }
}
public void DoSecondTask(int taskId)
{
this.Lock();
try
{
// imitating sync work from 3rd Party lib, that I need to make async
var t = Task.Run(() => {
Thread.Sleep(500); // sync work
});
... // doing some work here
t.Wait();
Console.WriteLine("Task {0}: DoSecondTask - complete", taskId);
}
finally { this.Unlock(); }
}
public void DoThirdTask(int taskId)
{
this.Lock();
try
{
// imitating sync work from 3rd Party lib, that I need to make async
var t = Task.Run(() => {
Thread.Sleep(500); // sync work
});
... // doing some work here
t.Wait();
Console.WriteLine("Task {0}: DoThirdTask - complete", taskId);
}
finally { this.Unlock(); }
}
}
// Worker class
public class Worker
{
private CommonWork CommonWork { get; set; }
public Worker(CommonWork commonWork)
{ this.CommonWork = commonWork; }
private void Loop()
{
while (true)
{
this.CommonWork.DoFirstTask(Task.CurrentId.Value);
if (Task.CurrentId.Value < 3)
{
this.CommonWork.DoSecondTask(Task.CurrentId.Value);
this.CommonWork.DoThirdTask(Task.CurrentId.Value);
}
}
}
public Task Start()
{
return Task.Run(() => this.Loop());
}
}
static void Main(string[] args)
{
var work = new CommonWork();
var client1 = new Worker(work);
var client2 = new Worker(work);
var client3 = new Worker(work);
client1.Start();
client2.Start();
client3.Start();
Console.ReadKey();
}
} // end of Program
解决方案是使用 new SempahoreSlim(1)
而不是 Mutex
(或简单的lock
,或Monitor
(。仅使用SemaphoreSlim
使Thread Scheduling
轮循机制,因此不会使某些线程/任务相对于其他线程"特殊"。谢谢我3阿农。
如果有人能评论为什么会这样,我将不胜感激。