基于 TPL 的循环服务:右工作者方法签名,异步
TPL-based looping service: right worker method signature, asynchrony
关于正确的工作方法签名,我需要了解以下内容:
- 对于
Worker
方法(如果要同步)returning Task
而不是 void
有什么意义吗?
- 我真的应该等待(调用
Wait()
)Worker
方法(如果要同步)吗?
- 当标记为 returning
Task
对象时,Worker
方法的 return 值应该是什么(如果要 sync/async 则两者都是)?
- 考虑到它完成的工作很长 运行 CPU/IO-bound 工作,
Worker
方法的签名和主体应该是什么?我应该跟随 this recommendation(如果去 mixed/async)吗?
备注
尽管有 cpu 绑定代码,但可以选择调用异步版本的 io 绑定方法(sql 查询)。所以它可能是全部同步或部分异步。至于Worker
方法中代码的性质。
public class LoopingService
{
private CancellationTokenSource cts;
// ..
void Worker(CancellationToken cancellationToken)
{
while(!cancellationToken.IsCancellationRequested)
{
// mixed, CPU/IO-bound code
try {
// sql query (can be called either as sync/async)
var lastId = documentService.GetLastDocument().Id;
// get next document from a public resource (third-party code, sync)
// can be moved to a web api
var document = thirdPartyDocumentService.GetNextDocument(lastId);
// apply different processors in parallel
var tasksList = new List<Task>();
foreach(var processor in documentService.Processors) {
// each processor checks if it's applicable
// which may include xml-parsing, additional db calls, regexes
// if it's applicable then document data is inserted into the db
var task = new Task(() => processor.Process(document));
tasksList.Add(task);
task.Start();
}
// or
// var tasksList = documentService.ProcessParallel(document);
Task.WaitAll(tasksList.ToArray(), cancellationToken);
}
catch(Exception ex) {
logger.log(ex);
}
}
}
public void Start()
{
this.cts = new CancellationTokenSource();
Task.Run(() => this.Worker(cts.Token));
}
public void Stop()
{
this.cts.Cancel();
this.cts.Dispose();
}
}
is there a point in returning Task instead of void for Worker method?
如果 Worker
是一个真正的异步方法,它应该 return 一个 Task
以便您能够等待它。如果它只是一个在后台线程上运行的同步方法,则没有必要将 return 类型从 void
更改为该方法不应该 return 任何东西。
what should be the return value of Worker
method when marked as returning Task
object?
没有。如果该方法是异步的并且标记为 async
且 return 类型为 Task
,则它不应 return 任何值:
async Task Worker(CancellationToken cancellationToken) { ... }
请注意,除非您实际在其中使用 await
关键字,否则将方法定义为 async
是没有意义的。
what signature and body of Worker
method should be given the work it completes is long-running CPU/IO-bound work? Should I follow this recommendation?
是的,可能吧。如果您出于某种原因在同一方法中同时执行异步和同步(CPU 绑定)工作,您应该更喜欢使用异步签名,但不要将同步内容包装在 Task.Run
中。那么您的服务将如下所示:
public class LoopingService
{
private CancellationTokenSource cts;
async Task Worker(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
await ...
}
}
public async Task Start()
{
this.cts = new CancellationTokenSource();
await this.Worker(cts.Token).ConfigureAwait(false);
}
public void Stop()
{
this.cts.Cancel();
this.cts.Dispose();
}
}
理想情况下,您的方法应该是异步的或 CPU 绑定的,但不能同时是两者。
关于正确的工作方法签名,我需要了解以下内容:
- 对于
Worker
方法(如果要同步)returningTask
而不是void
有什么意义吗? - 我真的应该等待(调用
Wait()
)Worker
方法(如果要同步)吗? - 当标记为 returning
Task
对象时,Worker
方法的 return 值应该是什么(如果要 sync/async 则两者都是)? - 考虑到它完成的工作很长 运行 CPU/IO-bound 工作,
Worker
方法的签名和主体应该是什么?我应该跟随 this recommendation(如果去 mixed/async)吗?
备注
尽管有 cpu 绑定代码,但可以选择调用异步版本的 io 绑定方法(sql 查询)。所以它可能是全部同步或部分异步。至于Worker
方法中代码的性质。
public class LoopingService
{
private CancellationTokenSource cts;
// ..
void Worker(CancellationToken cancellationToken)
{
while(!cancellationToken.IsCancellationRequested)
{
// mixed, CPU/IO-bound code
try {
// sql query (can be called either as sync/async)
var lastId = documentService.GetLastDocument().Id;
// get next document from a public resource (third-party code, sync)
// can be moved to a web api
var document = thirdPartyDocumentService.GetNextDocument(lastId);
// apply different processors in parallel
var tasksList = new List<Task>();
foreach(var processor in documentService.Processors) {
// each processor checks if it's applicable
// which may include xml-parsing, additional db calls, regexes
// if it's applicable then document data is inserted into the db
var task = new Task(() => processor.Process(document));
tasksList.Add(task);
task.Start();
}
// or
// var tasksList = documentService.ProcessParallel(document);
Task.WaitAll(tasksList.ToArray(), cancellationToken);
}
catch(Exception ex) {
logger.log(ex);
}
}
}
public void Start()
{
this.cts = new CancellationTokenSource();
Task.Run(() => this.Worker(cts.Token));
}
public void Stop()
{
this.cts.Cancel();
this.cts.Dispose();
}
}
is there a point in returning Task instead of void for Worker method?
如果 Worker
是一个真正的异步方法,它应该 return 一个 Task
以便您能够等待它。如果它只是一个在后台线程上运行的同步方法,则没有必要将 return 类型从 void
更改为该方法不应该 return 任何东西。
what should be the return value of
Worker
method when marked as returningTask
object?
没有。如果该方法是异步的并且标记为 async
且 return 类型为 Task
,则它不应 return 任何值:
async Task Worker(CancellationToken cancellationToken) { ... }
请注意,除非您实际在其中使用 await
关键字,否则将方法定义为 async
是没有意义的。
what signature and body of
Worker
method should be given the work it completes is long-running CPU/IO-bound work? Should I follow this recommendation?
是的,可能吧。如果您出于某种原因在同一方法中同时执行异步和同步(CPU 绑定)工作,您应该更喜欢使用异步签名,但不要将同步内容包装在 Task.Run
中。那么您的服务将如下所示:
public class LoopingService
{
private CancellationTokenSource cts;
async Task Worker(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
await ...
}
}
public async Task Start()
{
this.cts = new CancellationTokenSource();
await this.Worker(cts.Token).ConfigureAwait(false);
}
public void Stop()
{
this.cts.Cancel();
this.cts.Dispose();
}
}
理想情况下,您的方法应该是异步的或 CPU 绑定的,但不能同时是两者。