2016-08-04 93 views
0

背景

我目前正在通過終端界面重新創建具有GUI功能的GUI功能。所以錯誤不在觸發事件的另一端,因爲它以原始的GUI形式工作。如何使用異步事件管理線程阻塞和解除阻塞?

我在多臺機器上運行由子任務組成的任務。

我訂閱了事件,這些事件在進行時觸發並打印出描述性消息。 要爲所有Y機器的每個X子任務打印一條消息。

然後發生異步多線程操作。

我想打印出每個子任務的消息,每個子任務只解析一次。

我追蹤子任務的完成情況,並保留一個2D布爾數組,其中行是機器和列子任務。

問題

調試時,我可以看到正在進入下面的事件處理方法。運行numOfSubtasksFoundEventHandler中的print語句,但在設置AutoReset事件之前,會觸發多個BigTask事件,並在.WaitOne處被阻止。

但是,儘管之後運行了numOfSubtasksFound.Set(),但沒有其他任何內容被打印,程序也不會執行完畢。沒有任何東西能通過numOfSubtasksFound.WaitOne。

如果我在BigTaskHandler方法中取出numOfSubtasksFound.WaitOne,我會收到類似的行爲,但有幾條消息指出BigTask完成,然後程序停止在其他地方。

在這裏管理阻止和解除封鎖的最佳方法是什麼?還是有一個小的修復?

目標

我需要的是,直到numOfSubtasksFoundEventHandler已經運行一次,以阻止子任務的事件處理程序方法的操作方式。我只需要numOfSubTasksFoundEventHandler只運行一次。

當前子任務事件處理程序未正確解除阻止。切換大小寫代碼從未在numOfSubtasksFound.Set();後執行。運行。

//MAIN 
    bool[] machinesDoneTasks = new bool[numOfMachines]; 
    bool[][] machinesDoneSubtasks = new bool[numOfMachines][]; 

    try 
    { 
     //thread/task blocking 
     numOfSubtasksFound = new AutoResetEvent(false); 
     AllSubTasksDone = new AutoResetEvent(false); 
     AllBigTasksDone = new AutoResetEvent(false); 

     //Subscribe to events to get number of subtasks and print useful information as tasks progress 
     numOfSubtasksFoundEvent += numOfSubtasksFoundEventHandler; 
     SubTaskProgEvent += SubTaskEventProgHandler; //prog stands for progress 
     BigTaskProgEvent += BigTaskProgEventHandler; 

     RunAllTasksOnAllMachines();//this will trigger the events above 

     //Don't exit program until those descriptive messages have been printed 
     numOfSubtasksFound.WaitOne(); 
     AllSubTasksDone.WaitOne(); 
     //SubTaskProgEvent -= SubTaskProgEventHandler; 
     AllBigTasksDone.WaitOne(); 
     //BigTaskProgEvent -= BigTaskProgEventHandler; 
    } 
    catch (Exception e) 
    { 
     //print exceptions 
    } 
    //END MAIN 

下面不一定是第一個要觸發的事件。

internal void numOfSubtasksFoundEventHandler(object sender, EventArgs e) 
{ 
    //get number of subtasks from args after checking for nulls, empty arrays 

    for (int i = 0; i < numOfSubtasks; i++) 
     machinesDoneSubtasks[i] = new bool[numOfSubtasks]; 

    Console.WriteLine("number of subtasks found"); 
    numOfSubtasksFoundEvent -= numOfSubtasksFoundEventHandler;//don't subscribe to event where we get this from anymore 

    if (numOfSubtasksFound != null) 
     numOfSubtasksFound.Set(); //stop blocking 
} 

子任務事件不一定在大任務事件之前得到處理。

internal void SubtaskEventProgHandler(object sender, EventArgs e) 
{ 
    //null, empty checks on args 

    //Wait until we know how many subtasks there are and the 2D boolean array is fully built 
    numOfSubtasksFound.WaitOne(); 

    switch (e.WhatHappened) 
    { 
     Case.TaskComplete: 

      Console.Write(e.Machine + " is done subtask " + e.subTask); 

      //logic to determine machine and subtask 
      machinesDoneSubtasks[machine][Subtask] = true; 

      if (AllSubTasksDone != null && machinesDoneSubtasks.OfType<bool>().All(x => x)) 
       AllSubTasksDone.Set(); //stop blocking when 2D array is all true 

      break; 
      //other cases, different prints, but same idea 
    }  
} 

BigTask進度事件發生在處理的開始中間和結束。我只打印出我想要的案例的細節。

internal void BigTaskProgEventHandler(object sender, EventArgs e) 
{ 
    //Wait until we know how many subtasks there are and the 2D boolean array is fully built before printing 
    numOfSubtasksFound.WaitOne(); 

    //null, empty exception checks 
    switch (e.WhatHappened) 
    { 
      Case.TaskComplete: 

      Console.Write(e.Machine + " is done task " + e.subTask); 

    //logic to determine machine 
    machinesDoneTasks[machine] = true; 

    if (AllBigTasksDone != null && machinesDoneTasks.All(x => x)) 
     AllBigTasksDone.Set(); 

    break; 
    } 
    //other cases, different prints, but same idea 
} 
+0

在.Net中管理異步操作的最佳方式是使用async/await。什麼是你的要求?你想解決什麼問題? – radianz

+1

你可以發佈'RunAllTask​​sOnAllMachines'的代碼嗎?另外,你能縮短你的問題嗎?我們不需要像「Asynchronous multithreading shenaniegans thenue」這樣的行。我還沒有弄清楚「通過非gui界面」。和「觸發事件的另一端」。手段。你需要重新閱讀你寫的內容,並確保它是可以理解的。 – Enigmativity

+0

@RobertoBastianic更新了描述,希望它更清晰。就像它在上面,我沒有列出的所有東西都能正常工作。 我會更新我的問題,嘗試使其更清晰。 – despair

回答

0

我的問題是,第一個事件觸發後,其他子任務事件處理程序事件將調用.WaitOne這將阻止。這可能會在發現子任務數量後發生。問題在於.Set只會被調用一次,並且永遠不會被解除阻塞。

因此,在發現子任務的數量時使用布爾標誌來設置,並且鎖定子任務事件處理程序是一種方法。

0

異步/等待模型的示例。 許多任務在每臺機器上運行並計算一個值。 完成所有任務後,值將顯示在控制檯上。

static void Main(string[] args) 
     { 
      var service = new DispatchTasksOnMachinesService(8, 3); 
      service.DispatchTasks(); 
      Console.Read(); 
     } 

     class DispatchTasksOnMachinesService 
     { 
      int numOfMachines; 
      int tasksPerMachine; 
      [ThreadStatic] 
      private Random random = new Random(); 

      public DispatchTasksOnMachinesService(int numOfMachines, int tasksPerMachine) 
      { 
       this.numOfMachines = numOfMachines; 
       this.tasksPerMachine = tasksPerMachine; 
      } 

      public async void DispatchTasks() 
      { 
       var tasks = new List<Task<Tuple<Guid, Machine, int>>>(); 
       for (int i = 0; i < this.numOfMachines; i++) 
       { 
        var j = i; 
        for (int k = 0; k < this.tasksPerMachine; k++) 
        { 
         var task = Task.Run(() => Foo(Guid.NewGuid(), new Machine("machine" + j))); 
         tasks.Add(task); 
        } 
       } 

       var results = await Task.WhenAll<Tuple<Guid, Machine, int>>(tasks); 
       foreach (var result in results) 
       { 
        Console.WriteLine($"Task {result.Item1} on {result.Item2} yielded result {result.Item3}"); 
       } 
      } 

      private Tuple<Guid, Machine, int> Foo(Guid taskId, Machine machine) 
      { 
       Thread.Sleep(TimeSpan.FromSeconds(random.Next(1,5))); 
       Console.WriteLine($"Task {taskId} has completed on {machine}"); 
       return new Tuple<Guid, Machine, int>(taskId, machine, random.Next(500, 2000)); 
      } 
     } 

     class Machine 
     { 
      public string Name { get; private set; } 

      public Machine(string name) 
      { 
       this.Name = name; 
      } 

      public override string ToString() 
      { 
       return this.Name; 
      } 
     }