多个任务中的一个在互斥体中获取锁定的时间比其他任务长得多

本文关键字:任务 时间 锁定 获取 其他 一个 | 更新日期: 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阿农。

如果有人能评论为什么会这样,我将不胜感激。