2011-01-13 84 views
2

我已经阅读了很多关于在.NET 4.0中新的任务功能的一个或多个对象,但是我还没有发现以下问题的解决方案:.NET 4.0的任务:同步上

我写一个服务器应用程序处理来自多个用户的请求,我想使用任务将这些请求分发到多个核心。但是,这些任务应该在对象上同步 - 对于开始的用户 - 这样每次只处理一个对象的任务。使用Task.ContinueWith()可以很容易实现,但也可以在多个对象上同步一个任务(例如,当用户将资金转移给另一个用户时,变量应该在用户A处递减并在用户B处递增没有其他任务干扰)。

所以,我的第一次尝试是接收委托,创建任务并将它们存储在字典中的类,并将对象作为关键字进行同步。如果计划了新任务,则可以使用Task.ContinueWith()将其附加到给定对象的最后一个任务。如果它应该在多个对象上同步,则使用TaskFactory.ContinueWhenAll()创建新任务。创建的任务存储在字典中,用于与其同步的每个对象。 这是我的第一稿:

public class ActionScheduler:IActionScheduler 
{ 
    private readonly IDictionary<object, Task> mSchedulingDictionary = new Dictionary<object, Task>(); 
    private readonly TaskFactory mTaskFactory = new TaskFactory(); 

    /// <summary> 
    /// Schedules actions synchonized on one or more objects. Only one action will be processed for each object at any time. 
    /// </summary> 
    /// <param name="synchronisationObjects">Array of objects the current action is synchronized on</param> 
    /// <param name="action">The action that will be scheduled and processed</param> 
    public void ScheduleTask(object[] synchronisationObjects, Action action) 
    {    
     // lock the dictionary in case two actions are scheduled on the same object at the same time 
     // this is necessary since reading and writing to a dictionary can not be done in an atomic manner 
     lock(mSchedulingDictionary) 
     { 
      // get all current tasks for the given synchronisation objects 
      var oldTaskList = new List<Task>(); 
      foreach (var syncObject in synchronisationObjects) 
      { 
       Task task; 
       mSchedulingDictionary.TryGetValue(syncObject, out task); 
       if (task != null) 
        oldTaskList.Add(task); 
      } 

      // create a new task for the given action 
      Task newTask; 
      if (oldTaskList.Count > 1) 
      { 
       // task depends on multiple previous tasks 
       newTask = mTaskFactory.ContinueWhenAll(oldTaskList.ToArray(), t => action()); 
      } 
      else 
      { 
       if (oldTaskList.Count == 1) 
       { 
        // task depends on exactly one previous task 
        newTask = oldTaskList[0].ContinueWith(t => action()); 
       } 
       else 
       { 
        // task does not depend on any previous task and can be started immediately 
        newTask = new Task(action); 
        newTask.Start(); 
       } 
      } 

      // store the task in the dictionary 
      foreach (var syncObject in synchronisationObjects) 
      { 
       mSchedulingDictionary[syncObject] = newTask; 
      } 
     } 
    } 
} 

如果一个任务“multiSyncTask”是为多个对象创建的,并为每个对象的事后任务计划为这甚至工作。因为它们都与multiSyncTask.ContinueWith()创建的,他们开始同步:

static void Main() 
    { 
     IActionScheduler actionScheduler = new ActionScheduler(); 

     var syncObj1 = new object(); 
     var syncObj2 = new object(); 

     // these two start and complete simultaneously: 
     actionScheduler.ScheduleTask(new[] { syncObj1 },() => PrintTextAfterWait("1")); 
     actionScheduler.ScheduleTask(new[] { syncObj2 },() => PrintTextAfterWait("2")); 
     // this task starts after the first two and "locks" both objects: 
     actionScheduler.ScheduleTask(new[] { syncObj1, syncObj2 },() => PrintTextAfterWait("1 and 2")); 
     // these two - again - start and complete simultaneously after the task above: 
     actionScheduler.ScheduleTask(new[] { syncObj1 },() => PrintTextAfterWait("1")); 
     actionScheduler.ScheduleTask(new[] { syncObj2 },() => PrintTextAfterWait("2")); 
    } 

    static void PrintTextAfterWait(string text) 
    { 
     Thread.Sleep(3000); 
     Console.WriteLine(text); 
    } 

你觉得 - 这是我的问题很好的解决方案?我对字典上的大锁有点怀疑,但是如果两个任务同时安排在一个对象上以防止竞争条件,就很有必要。当然,字典只是在创建任务所需的时间内被锁定,而不是在处理时。

此外,我很想知道是否有任何已有的解决方案或编码范例,可以更好地解决我的问题,使用.net 4.0我没有找到的任务。

谢谢你和最诚挚的问候, 约翰内斯

+0

是的,不知道锁;它包装了两个`for-each`循环。 – IAbstract 2011-01-13 19:05:36

回答

0

如果我给你正确的..你想有一个Task.ContinueWith(TASK1,TASK2,拉姆达)? 像CCR中的Join仲裁器? http://msdn.microsoft.com/en-us/library/bb648749.aspx 如果是这样,可能最优雅的选择是在TPL数据流中使用JoinBlock(http://www.microsoft.com/download/en/confirmation.aspx?id=14782)。 或者,您是否尝试将Task.WaitAll()用作您的依赖任务的第一条指令?