C# 检查线程/后台任务是否已完成或需要中止
我对console/windows服务(我可以在任一模式下运行)应用程序有一个相当简单的要求:C# 检查线程/后台任务是否已完成或需要中止,c#,.net,multithreading,task,C#,.net,Multithreading,Task,我对console/windows服务(我可以在任一模式下运行)应用程序有一个相当简单的要求: 从数据库中获取要处理的项的列表 在后台启动一个方法(只有一个,不需要更多)来处理一个项目 检查是否已完成或需要终止(通过sql查找) 在完成/中止下一个步骤时重复2 当没有更多的时候,睡一会儿,重复1 我对c#/.net相当陌生,我可以看到各种线程系统。在这种情况下,线程和任务哪个更好 在线程的情况下,我假设每个要处理的项目都类似于(此粗略代码): Thread thread = new MyThre
Thread thread = new MyThread(new ThreadStart(this.SomeFunction));
thread.Start();
while(!finished) {
if (!thread.IsAlive())
finished=true;
else {
//check database for early termination of job
terminate=SomeChdck();
if(terminate) { thread.Abort(); finished=true;}
}
}
//返回并重复
或者在任务的情况下,它类似于(同样,粗略地完成,并从网络上缩减):
//返回并重复
这些方法有什么不同吗(假设它们都能工作),我在某个地方读到Thread.Abort()被弃用了,但文档中没有提到
谢谢。使用任务工厂。您可以使用以下方法等待任务完成:
Task.WaitAll(task);
而不是示例代码中的while循环
此外,CancelationToken用于向任务发出信号,表示它应该取消,因此您需要自己执行检查
但是,如果你考虑的话,你会更好,这也支持取消令牌。通过这种方式,您可以从DB中检索记录,然后将它们作为IEnumerable传递到管道,同时您可以自由地从单独的线程或内部线程取消它们。您可以开始处理第一条记录,而其余记录则在后台检索。管道将为要处理的每个元素的每个步骤创建一个后台任务。每个步骤的默认并行度为1。它非常快速和高效
更新 带有简洁、并行扩展、附加和反应式扩展的小示例var pipeline = Pipeline.Create<SomeType, bool>(st =>
{
//Do something with st
return someBool; //some bool if you succeeded or not
});
var cts = new CancellationTokenSource();
//cancel after 10s (just for fun)
Observable.Timer(TimeSpan.FromSeconds(10)).Subscribe(s => cts.Cancel());
using (var conn = new SqlConnection("someConnectionString"))
{
conn.Open();
pipeline.Process(conn.Query<SomeType>("SOME SQL HERE", buffered:true),cts.Token).ToList();
}
var pipeline=pipeline.Create(st=>
{
//和圣做点什么
return someBool;//如果成功或失败,则返回someBool
});
var cts=新的CancellationTokenSource();
//10秒后取消(只是为了好玩)
TimeSpan.FromSeconds(10)).Subscribe(s=>cts.Cancel());
使用(var conn=newsqlconnection(“someConnectionString”))
{
conn.Open();
Process(conn.Query(“这里有些SQL”,缓冲:true),cts.Token.ToList();
}
选择此选项的原因是为了演示使用Dapper是多么容易,并行扩展Extras是多么强大和方便,但对于您的示例,它是故意设计过度的…:)我希望你能原谅我。需要结尾处的ToList(),否则不会对IEnumerable执行任何操作。或者,您可以使用以下方法:
Console.WriteLine(
pipeline.Process(conn.Query<SomeType>("SOME SQL HERE", buffered: true), cts.Token).All(b => b)
? "All records processed successfully"
: "Some records failed");
Console.WriteLine(
Process(conn.Query(“这里有些SQL”,缓冲:true),cts.Token).All(b=>b)
?“已成功处理所有记录”
:“某些记录失败”);
如果要从数据处理步骤内部取消,请首先声明cts:
var cts = new CancellationTokenSource();
var pipeline = Pipeline.Create<SomeType,bool>(st =>
{
//Do something with st
//you could even cancel from here
if(someOtherBool)
cts.Cancel();
return someBool; //some bool if you succeeded or not for example
});
var cts=new CancellationTokenSource();
var pipeline=pipeline.Create(st=>
{
//和圣做点什么
//你甚至可以从这里取消
如果(其他人)
cts.Cancel();
返回someBool;//例如,如果成功或失败,则返回someBool
});
如果不想声明特定类型:
var cts = new CancellationTokenSource();
var pipeline = Pipeline.Create<dynamic,bool>(d =>
{
//Do something with data
if(someOtherBool)
cts.Cancel();
return someBool; //some bool if you succeeded or not
});
using (var conn = new SqlConnection("someConnectionString"))
{
conn.Open();
foreach (var b in pipeline.Process(conn.Query("SOME SQL HERE", buffered: true), cts.Token))
{
Console.WriteLine(b?"Success":"Failure");
}
}
var cts=new CancellationTokenSource();
var pipeline=pipeline.Create(d=>
{
//处理数据
如果(其他人)
cts.Cancel();
return someBool;//如果成功或失败,则返回someBool
});
使用(var conn=newsqlconnection(“someConnectionString”))
{
conn.Open();
foreach(pipeline.Process(conn.Query(“这里有些SQL”,缓冲:true),cts.Token)中的var b)
{
控制台写入线(b?“成功”:“失败”);
}
}
最后要提到的一点是,cts.Cancel()通常会在内部线程上引发异常,因此如果需要,请将管道封装在try/catch中
更新2
在阅读了作者的评论后,我仍然选择了简洁、小便和Rx(双关语)的组合
var cts=new CancellationTokenSource();
var pipeline=pipeline.Create(d=>
{
//对步骤1中的数据执行一些操作
如果(某些条件检查)
cts.Cancel();
返回d;
}).Next(d=>
{
//第二步是处理数据
如果(某些条件检查)
cts.Cancel();
返回d;
});
订阅=可观察的时间间隔(TimeSpan.FromMinutes(1))。订阅(>
{
尝试
{
使用(var conn=newsqlconnection(“someConnectionString”))
{
conn.Open();
foreach(pipeline.Process(conn.Query(“此处的一些SQL”,缓冲:true),cts.Token)中的var v)
{
//对结果做某事或忽略结果
}
}
}
捕获(聚合异常e)
{
//调查发生的情况,可能是处理中的错误
//或手术取消
}
捕获(例外e)
{
//所有其他例外情况
}
});
Rx让我创建了一个整洁的可观察的,每分钟都会开火。我也可以设计一个在上一次运行一段时间不活动后触发的,在这种情况下我更喜欢interval
PEE让我创建一个整洁的工作流,在这里我可以指定对从数据库检索到的一个数据项执行的多个步骤。通过访问CancellationTokenSource,我可以在每个步骤完成后立即取消所有步骤,因此,如果一条记录在步骤1中,另一条记录在步骤N中,则这两条记录都将在各自的代码块完成后立即取消
Dapper在与数据库交谈时只是一个时间服务器,句号
然而,正如您所知,我并没有真正使用线程或任务,所以我在这里回答作者的问题吗?不是真的。相反,我为他提供了一个alterna
var cts = new CancellationTokenSource();
var pipeline = Pipeline.Create<dynamic,bool>(d =>
{
//Do something with data
if(someOtherBool)
cts.Cancel();
return someBool; //some bool if you succeeded or not
});
using (var conn = new SqlConnection("someConnectionString"))
{
conn.Open();
foreach (var b in pipeline.Process(conn.Query("SOME SQL HERE", buffered: true), cts.Token))
{
Console.WriteLine(b?"Success":"Failure");
}
}
var cts = new CancellationTokenSource();
var pipeline = Pipeline.Create<dynamic, dynamic>(d =>
{
//Do something with data in step 1
if (someConditionalCheck)
cts.Cancel();
return d;
}).Next<dynamic>(d =>
{
//do something with data is step 2
if(someConditionalCheck)
cts.Cancel();
return d;
});
subscription = Observable.Interval(TimeSpan.FromMinutes(1)).Subscribe(_ =>
{
try
{
using (var conn = new SqlConnection("someConnectionString"))
{
conn.Open();
foreach (var v in pipeline.Process(conn.Query("SOME SQL HERE", buffered: true), cts.Token))
{
//Do something with or ignore the result
}
}
}
catch (AggregateException e)
{
//Investigate what happened, could be error in processing
//or operation cancelled
}
catch (Exception e)
{
//All other exceptions
}
});