多个操作的并行化和结果的连接

时间:2012-07-11 11:40:36

标签: c# multithreading silverlight task-parallel-library

我想在使用任务并行库的 Silverlight 5.0 应用程序中实现以下功能(SL5具有任务工厂但没有Parallel.For)。我有很多线程知识但没有TPL,所以这似乎是一个很好的任务得到一些:)

目前我有一些代码,它们按如下方式同步执行:

public interface IProcessor
{
    IEnumerable<Bar> Provide(Foo param)
}

private IEnumerable<IProcessor> processors; 

public void DoMultiOperations(Foo param, Action<IEnumerable<Bar>> callback)
{
    List<Bar> allResults = new List<Bar>();

    foreach(var processor in this.processors)
    {
        allResults.AddRange(processor.Provide(param));
    }

    callback(allResults);
}

考虑每个IProcessor接受一个Foo参数,以Provide返回IEnumerable<Bar>。所有结果的聚合都通过回调发送回调用者。

现在一些IProcessors立即执行。有些人打电话给服务器,可能需要几秒钟。我想为N IProcessor个实例安排N个任务,当所有实例完成(或超时)时,连接IEnumerable<Bar>个结果。

如果可能的话,我想在整个操作中添加一个超时,所以如果所有内容都没有在15秒内完成,请抛出。

非常感谢您的帮助:)

5 个答案:

答案 0 :(得分:4)

我再次无法测试此代码,但如果Silverlight没有Parallel.ForEach你可以使用Task.WaitAll

    private IEnumerable<IProcessor> processors;

    public void DoMultiOperations(Foo param, Action<IEnumerable<Bar>> callback)
    {
        var allResults = new ConcurrentQueue<Bar>();
        Task.WaitAll(processors.Select(processor => Task.Factory.StartNew(() => GetData(processor, param, allResults))).ToArray());
        callback(allResults);
    }

    private static void GetData(IProcessor processor, Foo param, ConcurrentQueue<Bar> allResults)
    {
        var enumerable = processor.Provide(param);
        foreach (var bar in enumerable)
        {
            allResults.Enqueue(bar);
        }
    }

答案 1 :(得分:1)

我认为这大致正确

public void DoMultiOperations(Foo param, Action<IEnumerable<Bar>> callback)
{
    var allResults = new List<Bar>();

    // We are using all the default options on the TaskFactory 
    // except when we are appending the results this has to be synchronized 
    // as List<> is not multithreading safe (a more appropriate collection perhaps) 
    var taskFactory = new TaskFactory<IEnumerable<Bar>>(
        TaskCreationOptions.None,
        TaskContinuationOptions.ExecuteSynchronously);

    // Kick off a task for every processor
    var tasks =
        new List<Task<IEnumerable<Bar>>>(processors.Count());
    tasks.AddRange(
        processors.Select(
            processor =>
            taskFactory.StartNew(() => processor.Provide(param))));

    if (Task.WaitAll(tasks.ToArray(), 5 * 1000))
    {
        foreach (Task<IEnumerable<Bar>> task in tasks)
        {
            allResults.AddRange(task.Result);
        }
        callback(allResults);
    }
} 

答案 2 :(得分:1)

这将异步并行运行所有任务:

public void DoMultiOperations(Foo param, Action<IEnumerable<Bar>> callback)
{
    // since each task's callback would access this storage - we are using
    // one of the concurrent queue
    ConcurrentQueue<Bar> allResults = new ConcurrentQueue<Bar>();

    Task[] tasks = this.processors.Select(p => new Task(() =>
        {
            IEnumerable<Bar> results = p.Provide(param);
            foreach (var newItem in results)
            {
                allResults.Enqueue(newItem);
            }
        })).ToArray();

    foreach (var task in tasks)
    {
        task.Start();
    }

    // 5 seconds to wait or inject a value into this method
    Task.WaitAll(tasks, 5000);                
    callback(allResults);
}

答案 3 :(得分:1)

为什么不使用类似下面的内容

// Allow for cancellation.
CancellationTokenSource cancelSource = new CancellationTokenSource();
CancellationToken token = new CancellationToken();
TaskCreationOptions atp = TaskCreationOptions.AttachedToParent; 

List<Bar> allResults = new List<Bar>();
Task<List<Bar>> asyncTask = Task.Factory.StartNew<List<Bar>>(() => asyncMethod(token, atp), token);

// Continuation is returned when the asyncMethod is complete.
asyncTask.ContinueWith(task =>
{
    // Handle the result.
    switch (task.Status)
    {
        // Handle any exceptions to prevent UnobservedTaskException.             
        case TaskStatus.RanToCompletion:
            break;
        case TaskStatus.Canceled:
            break;
        case TaskStatus.Faulted:
    }
}

asyncMethod中,您可以执行以下操作

private List<Bar> asyncMethod(CancellationToken token)
{
    List<Bar> allResults = new List<Bar>();

    foreach(var processor in this.processors)
    {
        Task.Factory.StartNew<IEnumerable<Bar>>(() => 
        {     
            processor.Provide(param);
        }, atp).ContinueWith( cont => { allResults.AddRange(cont.Result) }); 

        // Cancellation requested from UI Thread.
        if (token.IsCancellationRequested)
            token.ThrowIfCancellationRequested();
    }
    return allResults;
}
然后,哟可以从第一个代码段中名为List<Bar>的续集中获得整体结果(task)。您通过UI(例如

)通过某些事件调用取消
// Cancellation requested from UI Thread.
if (token.IsCancellationRequested)
    token.ThrowIfCancellationRequested();

我无法测试这个,但上面的内容应该可行。有关更多信息和使用课程,请参阅此great introduction to TPL

我希望这是有用的。

答案 4 :(得分:0)

使用TPL,您可以传递循环状态,以通知其他线程在超时的情况下中止。您需要执行以下操作:

    public void DoMultiOperations(Foo param, Action<IEnumerable<Bar>> callback)
    {
        ConcurrentBag<Bar> allResults = new ConcurrentBag<Bar>();

        Stopwatch sw = new Stopwatch();
        sw.Start();

        Parallel.ForEach(this.processors, (processor, loopState) =>
        {
            foreach (Bar item in processor.Provide(param))
            {
                allResults.Add(item);
            }

            if (sw.ElapsedMilliseconds > 15000)
            {
                loopState.Stop();
                throw new TimeoutException();
            }
        });

        callback(allResults);
    }