多个任务之一获取Mutex中的锁定比其他任务更长

时间:2014-12-19 11:53:08

标签: c# multithreading concurrency task-parallel-library mutex

状况

目前在我的项目中,我有3个Workers内部有一个工作循环,还有一个CommonWork类对象,其中包含Work个方法( DoFirstTask,DoSecondTask,DoThirdTask < / {>)Workers可以调用。每个Work方法必须相互独立地执行其他方法。每个方法都会生成更多嵌套的Tasks,等待它们完成。

问题

当所有3 Workers开始时,2 Workers以相同的速度执行,但第3 Worker落后于第1 Worker是超级快,第二慢一点,第三慢很慢,这取决于现实世界。

BIZARRENESS

当只有2 Workers正在工作时,他们也能很好地分享工作,并以相同的速度执行。

更有趣的是,即使是第3 Worker次调用的CommonWork方法数量也会减少,并且有可能执行更多 循环 循环,它不是。我尝试在下面的代码中用条件模拟它:

if (Task.CurrentId.Value < 3)

调试时,我发现第3 Worker号等待获取Mutex上的锁定的时间比其他Workers长得多。有时,其他两个Workers只是互相交换,第三个继续等待Mutex.WaitOne();我想,如果没有真正输入它,因为其他Workers在获取锁定方面没有问题!

我已经尝试过什么

我尝试将Worker Tasks作为TaskCreateOptions.LongRunning启动,但没有任何改变。我还尝试通过指定Tasks使嵌套 Tasks成为孩子 TaskCreateOpions.AttachedToParent,认为它可能与 local相关队列和日程安排,但显然不是。

简化代码

以下是我的实际应用程序的简化代码。可悲的是,我不能在这个简单的例子重现这种情况:

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

1 个答案:

答案 0 :(得分:0)

解决方案是使用new SempahoreSlim(1)代替Mutex(或简单lockMonitor)。仅使用SemaphoreSlim使Thread Scheduling成为循环,因此没有制作一些主题/任务&#34;特殊&#34;关于其他线程。谢谢I3arnon。

如果有人可以评论为什么会这样,我会很感激。