2017-05-26 334 views
0

我搞亂了多線程和製作某種任務引擎。這個想法是,引擎可以有一個可配置數量的線程在等待,當一個新任務到達時,第一個空閒線程將其拾取並執行。c#多個線程等待一個ManualResetEvent

問題是某些2線程以某種方式獲取相同的任務。我查看了一下,我認爲這段代碼應該可以工作,但顯然不適用。如果我現在將其註釋掉了10ms的睡眠時間,那麼它可以工作,但我不確定我是否理解了原因。它看起來像.Reset()函數在實際重置事件之前返回?

有人可以解釋一下嗎?有沒有更好的方法讓只有一個線程在有多個等待時繼續?

由於

using System; 
using System.Collections.Concurrent; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading; 
using System.Threading.Tasks; 

namespace TaskTest 
{ 
    public class Engine 
    { 
     private ManualResetEvent taskEvent; 
     private ConcurrentQueue<Task> tasks; 
     private bool running; 
     private List<Thread> threads; 
     private int threadAmount; 
     private int threadsBusy = 0; 

     public Engine(int amountOfThreads) 
     { 
      taskEvent = new ManualResetEvent(false); 
      tasks = new ConcurrentQueue<Task>(); 
      threads = new List<Thread>(); 

      threadAmount = amountOfThreads; 
     } 

     public void Start() 
     { 
      running = true; 
      for (var i = 0; i < threadAmount; i++) 
      { 
       var thread = new Thread(Process); 
       thread.Name = "Thread " + i; 
       threads.Add(thread); 
       thread.Start(); 
      } 
     } 

     public void Stop() 
     { 
      running = false; 
      taskEvent.Set(); 
      threads.ForEach(t => t.Join()); 
     } 

     private void Process() 
     { 
      while (running) 
      { 
       lock (taskEvent) 
       { 
        // Lock it so only a single thread is waiting on the event at the same time 
        taskEvent.WaitOne(); 
        taskEvent.Reset(); 
        //Thread.Sleep(10); 
       } 

       if (!running) 
       { 
        taskEvent.Set(); 
        return; 
       } 

       threadsBusy += 1; 
       if (threadsBusy > 1) 
        Console.WriteLine("Failed"); 

       Task task; 
       if (tasks.TryDequeue(out task)) 
        task.Execute(); 

       threadsBusy -= 1; 
      } 
     } 

     public void Enqueue(Task t) 
     { 
      tasks.Enqueue(t); 
      taskEvent.Set(); 
     } 
    } 
} 

EDIT 休息的代碼:

namespace TaskTest 
{ 
    public class Start 
    { 
     public static void Main(params string[] args) 
     { 
      var engine = new Engine(4); 
      engine.Start(); 

      while (true) 
      { 
       Console.Read(); 
       engine.Enqueue(new Task()); 
      } 
     } 
    } 
} 


namespace TaskTest 
{ 
    public class Task 
    { 
     public void Execute() 
     { 
      Console.WriteLine(Thread.CurrentThread.Name); 
     } 
    } 
} 
+0

你想鎖定(OBJ)? – farmer1992

+0

我不確定你的意思? –

+0

這並不直接回答你的問題,但你應該改用BlockingCollection。它已經在做你正在做的事情,除了它可以工作 –

回答

0

當在按鍵用Console.Read(),兩個字符從輸入讀出。您應該使用Console.ReadLine()

請注意,您的代碼可以通過使用BlockingCollection處理同步進行簡化了很多:

public class Engine 
{ 
    private BlockingCollection<Task> tasks; 
    private List<Thread> threads; 
    private int threadAmount; 

    public Engine(int amountOfThreads) 
    { 
     tasks = new BlockingCollection<Task>(); 
     threads = new List<Thread>(); 

     threadAmount = amountOfThreads; 
    } 

    public void Start() 
    { 
     for (var i = 0; i < threadAmount; i++) 
     { 
      var thread = new Thread(Process); 
      thread.Name = "Thread " + i; 
      threads.Add(thread); 
      thread.Start(); 
     } 
    } 

    public void Stop() 
    { 
     tasks.CompleteAdding(); 
     threads.ForEach(t => t.Join()); 
    } 

    private void Process() 
    { 
     foreach (var task in tasks.GetConsumingEnumerable()) 
     { 
      task.Execute(); 
     } 
    } 

    public void Enqueue(Task t) 
    { 
     tasks.Add(t); 
    } 
}