我需要在控制台应用程序中运行多个异步任务,并在进一步处理之前等待它们全部完成。
那里有很多文章,但我读的越多越好。我已经阅读并理解了任务库的基本原理,但我明显错过了某个链接。
我知道可以将任务链接起来,以便它们在另一个完成后开始(这几乎是我读过的所有文章的场景),但我希望我的所有任务都在同时,我想知道他们一旦完成了。
对于这样的场景,最简单的实现是什么?
答案 0 :(得分:362)
这两个答案都没有提及等待的Task.WhenAll
:
var task1 = DoWorkAsync();
var task2 = DoMoreWorkAsync();
await Task.WhenAll(task1, task2);
Task.WaitAll
和Task.WhenAll
之间的主要区别在于前者将阻止(类似于在单个任务上使用Wait
)而后者将不会并且可以等待,从而产生控制回到调用者,直到所有任务完成。
更多的是,异常处理有所不同:
<强> Task.WaitAll
强>:
至少有一个Task实例被取消 - 或者 - 在执行至少一个Task实例期间抛出了异常。如果任务被取消,则AggregateException在其InnerExceptions集合中包含OperationCanceledException。
<强> Task.WhenAll
强>:
如果任何提供的任务在故障状态下完成,则返回的任务也将在Faulted状态下完成,其异常将包含来自每个提供的任务的一组未解包的异常的聚合。
如果提供的任务都没有出现故障但至少其中一个被取消,则返回的任务将以“已取消”状态结束。
如果没有任何任务出现故障而且没有任何任务被取消,则生成的任务将以RanToCompletion状态结束。 如果提供的array / enumerable不包含任务,则返回的任务将在返回给调用者之前立即转换到RanToCompletion状态。
答案 1 :(得分:91)
您可以创建许多任务,例如:
List<Task> TaskList = new List<Task>();
foreach(...)
{
var LastTask = new Task(SomeFunction);
LastTask.Start();
TaskList.Add(LastTask);
}
Task.WaitAll(TaskList.ToArray());
答案 2 :(得分:23)
我见过的最佳选择是以下扩展方法:
public static Task ForEachAsync<T>(this IEnumerable<T> sequence, Func<T, Task> action) {
return Task.WhenAll(sequence.Select(action));
}
这样称呼:
await sequence.ForEachAsync(item => item.SomethingAsync(blah));
或使用异步lambda:
await sequence.ForEachAsync(async item => {
var more = await GetMoreAsync(item);
await more.FrobbleAsync();
});
答案 3 :(得分:15)
您可以使用WhenAll
,它会返回一个没有返回类型的等待Task
或WaitAll
,并会阻止进一步执行Thread.Sleep
的代码执行,直到所有任务完成,取消或出现故障。
示例强>
var tasks = new Task[] {
TaskOperationOne(),
TaskOperationTwo()
};
Task.WaitAll(tasks);
// or
await Task.WhenAll(tasks);
如果你想以特定的顺序运行任务,你可以从this anwser获得灵感。
答案 4 :(得分:8)
您想链接Task
,还是可以并行调用?
用于链接
做一些像
Task.Run(...).ContinueWith(...).ContinueWith(...).ContinueWith(...);
Task.Factory.StartNew(...).ContinueWith(...).ContinueWith(...).ContinueWith(...);
并且不要忘记检查每个Task
中的前一个ContinueWith
实例,因为它可能会出现故障。
以并行方式
我遇到的最简单的方法:Parallel.Invoke
否则会Task.WaitAll
或者您甚至可以使用WaitHandle
来对剩余的零操作进行倒计时(等待,有新的课程:CountdownEvent
),或者。 ..
答案 5 :(得分:4)
这就是我使用数组 Func&lt;&gt; 的方式:
var tasks = new Func<Task>[]
{
() => myAsyncWork1(),
() => myAsyncWork2(),
() => myAsyncWork3()
};
await Task.WhenAll(tasks.Select(task => task()).ToArray()); //Async
Task.WaitAll(tasks.Select(task => task()).ToArray()); //Or use WaitAll for Sync
答案 6 :(得分:3)
还有另一个答案...但是我通常会遇到这种情况,当我需要同时加载数据并将其放入变量中时,例如:
var cats = new List<Cat>();
var dog = new Dog();
var loadDataTasks = new Task[]
{
Task.Run(async () => cats = await LoadCatsAsync()),
Task.Run(async () => dog = await LoadDogAsync())
};
try
{
await Task.WhenAll(loadDataTasks);
}
catch (Exception ex)
{
// handle exception
}
答案 7 :(得分:1)
应该有比接受的答案更简洁的解决方案。同时执行多个任务并获得其结果应该不需要三个步骤。
这是将方法简化为两步的方法:
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=MyKey"></script>
<script type="text/javascript">
function initialize() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(showPosition, showError);
}
function showPosition(position) {
var currentLatLong = position.coords;
var mapLatLong = parseFloat(position.coords).value;
var mapOptions = {
zoom: 13,
center: mapLatLong,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById('map'), mapOptions);
var directionsService = new google.maps.DirectionsService;
var directionsDisplay = new google.maps.DirectionsRenderer;
addMarker(currentLatLong.latitude, currentLatLong.longitude, "You are here");
$.ajax({
cache: false,
type: "POST",
url: "@Url.Action("GetNearbyLocation")",
dataType: "json",
contetType: "application/json; charset=utf-8",
data: { "latitude": currentLatLong.latitude, "longitude": currentLatLong.longitude },
success: function (data) {
$.each(data, function (i, item) {
addMarker(item.latitude, item.longitude, item.name);
});
},
error: function () {
alert("There was an error processing your request. Please try again later");
}
});
function addMarker(x, y, locationName) {
var infowindow = new google.maps.InfoWindow({
content: locationName
});
var location = new google.maps.LatLng(x, y);
var marker = new google.maps.Marker({
position: location,
map: map,
title: locationName,
});
infowindow.open(map, marker);
marker.addListener('click', function () {
infowindow.open(map, marker);
calculateAndDisplayRoute(directionsService, directionsDisplay, x, y);
});
}
function calculateAndDisplayRoute(directionsService, diresctionsDisplay, x, y) {
var latLongSource = { lat: parseFloat(currentLatLong.latitude), lng: parseFloat(currentLatLong.longitude) };
var latLongDestination = { lat: parseFloat(x), lng: parseFloat(y) };
directionsService.route({
origin: latLongSource,
destination: latLongDestination,
travelMode: "DRIVING"
}, function (response, status) {
if (status == "OK") {
directionDisplay.setDirections(response);
}
else {
alert("Directions not available at the moment. Please try again later.");
}
});
}
}
function showError(error) {
if (error.code == 1) {
alert("User denied the request for Geolocation");
}
else if (error.code == 2) {
alert("Location information is unavailable");
}
else {
alert("There was an error processing requesting your location");
}
}
}
google.maps.event.addDomListener(window, 'load', initialize)
</script>
您可以像这样使用它:
public async Task<Tuple<T1, T2>> WhenAllGeneric<T1, T2>(Task<T1> task1, Task<T2> task2)
{
await Task.WhenAll(task1, task2);
return Tuple.Create(task1.Result, task2.Result);
}
这消除了对临时任务变量的需要。使用此方法的问题是,尽管它可以同时执行两个任务,但您需要针对三个任务或任何其他数量的任务进行更新。如果其中一项任务未返回任何内容,它也将无法正常工作。确实,.Net库应该提供可以做到这一点的东西
答案 8 :(得分:-1)
我准备了一段代码来向您展示如何在某些情况下使用该任务。
// method to run tasks in a parallel
public async Task RunMultipleTaskParallel(Task[] tasks) {
await Task.WhenAll(tasks);
}
// methode to run task one by one
public async Task RunMultipleTaskOneByOne(Task[] tasks)
{
for (int i = 0; i < tasks.Length - 1; i++)
await tasks[i];
}
// method to run i task in parallel
public async Task RunMultipleTaskParallel(Task[] tasks, int i)
{
var countTask = tasks.Length;
var remainTasks = 0;
do
{
int toTake = (countTask < i) ? countTask : i;
var limitedTasks = tasks.Skip(remainTasks)
.Take(toTake);
remainTasks += toTake;
await RunMultipleTaskParallel(limitedTasks.ToArray());
} while (remainTasks < countTask);
}
答案 9 :(得分:-1)
如果您使用 async/await pattern,您可以像这样并行运行多个任务:
?ga