.net 4.0任务:在一个或多个对象上同步

时间:2011-01-13 18:00:55

标签: multithreading .net-4.0 synchronization task

我已经阅读了很多关于.net 4.0中新任务功能的内容,但我没有找到解决以下问题的方法:

我正在编写一个处理来自许多用户的请求的服务器应用程序,我想使用Tasks在多个核心上分发这些请求。但是,这些任务应该在对象上同步 - 对于开头,用户 - 这样一次只能为每个对象处理一个任务。使用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我无法追踪的任务来解决我的问题。

谢谢你,并致以最诚挚的问候, 约翰内斯

1 个答案:

答案 0 :(得分:0)

如果我说对了..你想要一个Task.ContinueWith(task1,task2,lambda)? 像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()作为依赖任务的第一条指令?