我正在编写一组异步任务,这些任务会消除下载和解析数据,但是我在下一步更新数据库时遇到了一些空白。
问题是,为了性能,我使用TableLock加载相当大的数据集,所以我想要做的是让我的导入服务等待第一个Task返回,开始导入。如果在第一次导入运行时另一个任务完成,则该进程将加入队列并等待任务1的导入服务完成。
例如
异步 - 任务1 - 任务2 - 任务3
同步 - ImportService
RunAsync Tasks
Task3 returns first > ImportService.Import(Task3)
Task1 return, ImportService is still running. Wait()
ImportService.Complete() event
Task2 returns. Wait()
ImportService.Import(Task1)
ImportService.Complete() event
ImportService.Import(Task2)
ImportService.Complete() event
希望这是有道理的!
答案 0 :(得分:2)
你不能在这里真正使用等待,但你可以等待多个任务完成:
var tasks = new List<Task)();
// start the tasks however
tasks.Add(Task.Run(Task1Function);
tasks.Add(Task.Run(Task2Function);
tasks.Add(Task.Run(Task2Function);
while (tasks.Count > 0)
{
var i = Task.WaitAny(tasks.ToArray()); // yes this is ugly but an array is required
var task = tasks[i];
tasks.RemoveAt(i);
ImportService.Import(task); // do you need to pass the task or the task.Result
}
然而,对我来说,应该有更好的选择。您可以让任务和导入运行并在ImportService
部分添加锁定,例如:
// This is the task code doing whatever
....
// Task finishes and calls ImportService.Import
lock(typeof(ImportService)) // actually the lock should probably be inside the Import method
{
ImportService.Import(....);
}
有些事情困扰着我的需求(包括使用静态的ImportService,静态类很少是一个好主意),但没有进一步的细节,我无法提供更好的建议。
答案 1 :(得分:1)
虽然这可能不是最优雅的解决方案,但我会尝试启动工作任务并让他们将输出放在ConcurrentQueue中。您可以检查队列是否在计时器上工作,直到完成所有任务。
var rand = new Random();
var importedData = new List<string>();
var results = new ConcurrentQueue<string>();
var tasks = new List<Task<string>>
{
new Task<string>(() =>
{
Thread.Sleep(rand.Next(1000, 5000));
Debug.WriteLine("Task 1 Completed");
return "ABC";
}),
new Task<string>(() =>
{
Thread.Sleep(rand.Next(1000, 5000));
Debug.WriteLine("Task 2 Completed");
return "FOO";
}),
new Task<string>(() =>
{
Thread.Sleep(rand.Next(1000, 5000));
Debug.WriteLine("Task 3 Completed");
return "BAR";
})
};
tasks.ForEach(t =>
{
t.ContinueWith(r => results.Enqueue(r.Result));
t.Start();
});
var allTasksCompleted = new AutoResetEvent(false);
new Timer(state =>
{
var timer = (Timer) state;
string item;
if (!results.TryDequeue(out item))
return;
importedData.Add(item);
Debug.WriteLine("Imported " + item);
if (importedData.Count == tasks.Count)
{
timer.Dispose();
Debug.WriteLine("Completed.");
allTasksCompleted.Set();
}
}).Change(1000, 100);
allTasksCompleted.WaitOne();