1

СИТУАЦИЯОдин из нескольких задач приобретает замок в Mutex гораздо дольше, чем другие задачи сделать

В настоящее время в моем проекте у меня есть 3 Workers, которые имеют рабочую петлю внутри, и один CommonWork объект класса, который содержит Work методы (DoFirstTask, DoSecondTask, DoThirdTask), который может вызвать Workers. Каждый метод Work должен выполняться взаимно исключительно по отношению друг к другу. Каждый из методов порождает больше вложенных Tasks, которые ждут, пока они не закончатся.

ПРОБЛЕМА

Когда все 3 Workers запущены, 2 Workers выполняют несколько с той же скоростью, но третий Worker отстает или первый Worker супер-быстрый, второй немного медленнее, и третий является очень медленно, это зависит от реального мира.

странностей

Когда только 2 Workers работают, они разделяют работу хорошо тоже, и выполнять с той же скоростью.

Что более интересно, что даже третий Worker вызывает меньшее число CommonWork методов, и имеет потенциал для выполнения более цикла циклов, это не так. Я пытался имитировать, что в приведенном ниже коде с условием:

if (Task.CurrentId.Value < 3) 

При отладке, я обнаружил, что третий Worker ждет на приобретение блокировку на Mutex значительно дольше, чем другие Workers. Иногда другие два Workers просто работают взаимозаменяемо, а третий продолжает ждать Mutex.WaitOne(); Наверное, не введя его, потому что другие Workers не имеют проблем с приобретением этого замка!

ЧТО Я УЖЕ ПРОБОВАЛИ

Я попытался начиная WorkerTasks, как TaskCreateOptions.LongRunning, но ничего не изменилось. Я также попытался сделать вложеннуюTasks быть ребенокTasks, указав TaskCreateOpions.AttachedToParent, думая, что это может быть связано с локальных очередей и планирования, но, видимо, это не так.

УПРОЩЕННЫЙ КОД

Ниже приведен упрощенный код моего реального приложения. Грустный сказать, я не мог воспроизвести эту ситуацию в этом простом примере:

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

Не используйте 'Task.Run', чтобы получить поток ThreadPool, а затем' Wait', чтобы заблокировать его. – i3arnon

+0

@ I3arnon, не уверен, что вы имеете в виду. Мне нужно, чтобы эти «Задачи» ожидались до того, как «Рабочий» может перейти к следующему методу task =. Все в порядке с этим. – Gabrielius

+2

Вам вообще не нужны эти задачи, потому что вы тратите поток, ожидающий их завершения. Просто попросите поток выполнить работу внутри задачи. – i3arnon

ответ

0

Решение было использовать new SempahoreSlim(1) вместо Mutex (или просто lock или Monitor).Только с использованием SemaphoreSlim, сделанного Thread Scheduling, должно быть round-robin, и поэтому не вносило некоторые темы/задачи «специальные» в отношении других потоков. Спасибо I3arnon.

Если кто-то может прокомментировать, почему это так, я был бы признателен.

Смежные вопросы