代码之家  ›  专栏  ›  技术社区  ›  lexeme

基于tpl的循环服务:正确的工作方法签名,异步

  •  1
  • lexeme  · 技术社区  · 7 年前

    关于正确的worker方法签名,我需要了解以下内容:

    • 回来有意义吗 Task 而不是 void 对于 Worker 方法(如果要同步)?
    • 我真的该等吗 Wait() )关于 工人 方法(如果要同步)?
    • 什么应该是返回值 工人 方法时标记为返回 任务 对象(如果要同步/异步,两者都是)?
    • 什么签名和正文 工人 方法应该是,假设它完成的工作是长时间运行的CPU/IO绑定工作?我应该跟着吗 this recommendation (如果要混合/异步)?

    注意

    尽管有CPU绑定的代码,但是可以选择调用异步版本的IO绑定方法(SQL查询)。所以它可能全部同步或部分异步。至于 工人 方法。


    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();
        }
    
    }
    
    1 回复  |  直到 7 年前
        1
  •  3
  •   mm8    7 年前

    返回task而不是void for worker方法有意义吗?

    如果 Worker 是一个真正的异步方法,它应该返回 Task 让你能够等待。如果它只是在后台线程上运行的同步方法,则没有必要更改返回类型 void 前提是该方法不应返回任何内容。

    什么应该是返回值 工人 方法时标记为返回 任务 反对?

    没有什么。如果该方法是异步的并标记为 async 返回类型为 任务 ,它不应返回任何值:

    async Task Worker(CancellationToken cancellationToken) { ... }
    

    注意,没有必要将方法定义为 异步 除非你真的使用 await 关键字。

    什么签名和正文 工人 方法应该给出它完成的工作是长时间运行的CPU/IO绑定工作吗?我应该遵循这个建议吗?

    可能是的。如果出于某种原因,您在同一方法中同时执行异步和同步(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绑定的,但不能两者都是。