2016-08-04 99 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; 
      } 
     }