如果其中一个线程失败,有没有办法停止执行其他并发线程?我正在开发一个ASP.NET MVC网站,它将大量使用API调用。我不是以顺序方式发送太多的API请求,而是创建线程,每个线程都消耗一个API调用。如果任何线程失败,我想杀死其他线程。我怎样才能做到这一点?我使用Task.WaitAll
但它允许所有其他线程执行,即使一个线程失败。请帮忙。
更新: 为了模拟实时场景,我创建了一个控制台应用程序。请参考以下代码。我想让第五个线程失败。第五个线程失败后,我希望所有其他正在运行的线程停止。
public class Program
{
static int[] delays = { 3, 2, 10, 4, 5, 6, 7, 8, 9, 1 };
static int[] ids = new int[10];
static Task[] tasks = null;
static CancellationTokenSource tokenSource = new CancellationTokenSource();
static IList<int> threadsExecuted = new List<int>();
static void Main(string[] args)
{
ids = Enumerable.Range(1, 10).ToArray();
try
{
tasks = ids.Select(id => MyTaks(id)).ToArray();
Task.WaitAll(tasks);
}
catch (Exception exception)
{
Console.WriteLine("Exception in Main::\nMessage: " + exception.Message +
"StackTrace: " + exception.StackTrace +
"InnerException: " + exception.InnerException);
}
Console.WriteLine("\n\nThreads executed: " + string.Join(", ", threadsExecuted.OrderBy(id => id).ToArray()));
Console.WriteLine("\n\n\nExit..");
Console.Read();
}
private static async Task MyTaks(int id)
{
var delay = delays[id - 1] * 1000;
Console.WriteLine("Thread id #" + id + " started with delay " + delay + " seconds.");
CancellationToken cToken = tokenSource.Token;
Task task = new Task(() =>
{
Thread.Sleep(delay);
if (id == 5) //Fail
{
Console.WriteLine("Cancelling..");
throw new Exception("Thread id #" + id + " failed.");
}
}, cToken);
task.Start();
await task;
Console.WriteLine("Thread id #" + id + " executed.");
threadsExecuted.Add(id);
}
}
答案 0 :(得分:2)
我不是以顺序方式发送太多API请求,而是创建线程,每个线程都使用一次API调用。
这种方法几乎会破坏服务器的任何可扩展性。您应该使用异步并发(多个操作)而不是并行并发(多个线程)。您的API代码将使用HttpClient.GetAsync
并传递CancellationToken
。
所以,你的测试代码看起来像这样:
static void Main(string[] args)
{
ids = Enumerable.Range(1, 10).ToArray();
try
{
tasks = ids.Select(id => MyTaks(id)).ToArray();
Task.WaitAll(tasks);
}
catch (Exception exception)
{
Console.WriteLine("Exception in Main::\nMessage: " + exception.Message +
"StackTrace: " + exception.StackTrace +
"InnerException: " + exception.InnerException);
}
lock (threadsExecuted)
Console.WriteLine("\n\nThreads executed: " + string.Join(", ", threadsExecuted.OrderBy(id => id).ToArray()));
Console.WriteLine("\n\n\nExit..");
Console.Read();
}
private static async Task MyTaks(int id)
{
var delay = delays[id - 1] * 1000;
Console.WriteLine("Task id #" + id + " started with delay " + delay + " seconds.");
CancellationToken cToken = tokenSource.Token;
// Production code would use `await httpClient.GetAsync(url, token)` here.
await Task.Delay(delay, cToken);
if (id == 5) //Fail
{
Console.WriteLine("Cancelling..");
tokenSource.Cancel();
throw new Exception("Thread id #" + id + " failed.");
}
Console.WriteLine("Thread id #" + id + " executed.");
lock (threadsExecuted)
threadsExecuted.Add(id);
}
答案 1 :(得分:1)
如果要取消任务集合
,可以尝试这样的事情 // Define the cancellation token.
CancellationTokenSource source = new CancellationTokenSource();
CancellationToken token = source.Token;
Random rnd = new Random();
Object lockObj = new Object();
List<Task<int[]>> tasks = new List<Task<int[]>>();
TaskFactory factory = new TaskFactory(token);
for (int taskCtr = 0; taskCtr <= 10; taskCtr++) {
int iteration = taskCtr + 1;
tasks.Add(factory.StartNew( () => {
int value;
int[] values = new int[10];
for (int ctr = 1; ctr <= 10; ctr++) {
token.ThrowIfCancellationRequested();
lock (lockObj) {
value = rnd.Next(0,101);
}
if (value == 0) {
source.Cancel();
Console.WriteLine("Cancelling at task {0}", iteration);
break;
}
values[ctr-1] = value;
}
return values;
}, token));
}
try {
Task<double> fTask = factory.ContinueWhenAll(tasks.ToArray(),
(results) => {
Console.WriteLine("Calculating overall mean...");
long sum = 0;
int n = 0;
foreach (var t in results) {
foreach (var r in t.Result) {
sum += r;
n++;
}
}
return sum/(double) n;
} , token);
Console.WriteLine("The mean is {0}.", fTask.Result);
}
catch (AggregateException ae) {
foreach (Exception e in ae.InnerExceptions) {
if (e is TaskCanceledException)
Console.WriteLine("Unable to compute mean: {0}",
((TaskCanceledException) e).Message);
else
Console.WriteLine("Exception: " + e.GetType().Name);
}
}
finally {
source.Dispose();
}
更新:使用取消令牌取消网络API 您需要在API中接受取消令牌
[AsyncTimeout(150)]
[HandleError(ExceptionType = typeof(TimeoutException),
View = "TimeoutError")]
public async Task<ActionResult> GizmosCancelAsync(
CancellationToken cancellationToken )
{
ViewBag.SyncOrAsync = "Asynchronous";
var gizmoService = new GizmoService();
return View("Gizmos",
await gizmoService.GetGizmosAsync(cancellationToken));
}
返回Task<ActionResult>
的异步操作方法是可取消的,即当CancellationToken
提供一个参数时,它们会带有AsyncTimeout attribute
参数。
答案 2 :(得分:0)
您需要使用Task.WaitAll()的CancellationToken版本。