我想在使用任务并行库的 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秒内完成,请抛出。
非常感谢您的帮助:)
答案 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);
}