按任务调用异步操作

Posted

技术标签:

【中文标题】按任务调用异步操作【英文标题】:call asynchronous operation by task 【发布时间】:2017-12-16 11:37:02 【问题描述】:

我有 Duplex 和 Sessionfull 服务和一个操作:

public FindStudies_DTO_OUT FindStudies(FindStudies_DTO_IN findStudies_DTO_IN)
    
        var token = Token;
        List<Study_C> ret = new List<Study_C>();
        _dispatcherCallBack = OperationContext.Current.GetCallbackChannel<IDispatcherCallBack>();
        AnnuncedFindStudies += DispatcherService_AnnuncedFindStudies;
        AnnuncedSPError += DispatcherService_AnnuncedSPError;
        Parallel.ForEach(Cluster, sp =>
        
            //Blah blah
            OnAnnuncedSPError(new SPError_DTO()
            
                ServicePointName = sp.Name,
                ErrorMessage = "Ping failed for " + sp.Name
            );
            var result = new List<Study_C>();//Filled
            lock (ret)
            
                OnAnnounceFindStudies(new FindStudies_DTO()
                
                    ServicePointName = sp.Name,
                    Studies = result
                );
                ret.AddRange(result);
            
            //blah blah
        );
        return new FindStudies_DTO_OUT(ret.Sort(findStudies_DTO_IN.SortColumnName, findStudies_DTO_IN.SortOrderBy));
    

和在消费者端(通用处理程序中的 Web 应用程序):

var findTask = Task.Factory.StartNew(() =>
                    
                        DispatcherClient dispatcherClient = new DispatcherClient(new DispatcherCallBack(), "dispatcherEndPoint", Token, Global.Cluster);
                        dispatcherClient.AnnuncedSPError += DispatcherClient_AnnuncedSPError;
                        dispatcherClient.AnnuncedFindStudies += DispatcherClient_AnnuncedFindStudies;
                        var res = dispatcherClient.FindStudies(new FindStudies_DTO_IN(startIndex, numberOfRows, col.FromText(sortColumnName), sort.FromText(sortOrder), criteria, searchMatching));
                        studies = Studies;
                    );
                    findTask .Wait();

在客户端,当我在 var findTask 上设置断点时,一切正常,但是当我删除断点时,它只在第一次运行时就可以工作,但之后我得到以下异常:

InvalidOperationException:此时无法启动异步操作。异步操作只能在异步处理程序或模块内或在页面生命周期中的某些事件期间启动。如果在执行页面时发生此异常,请确保将页面标记为 。此异常还可能表示尝试调用“async void”方法,在 ASP.NET 请求处理中通常不支持该方法。相反,异步方法应该返回一个 Task,调用者应该等待它。

有人知道客户端应该如何实现吗?

提前致谢。

【问题讨论】:

【参考方案1】:

我不知道为什么我不能在客户端使用无限 wait,但从我读过的 here 来看,建议不要在同步上使用 asyc,反之亦然。

无论如何我已经通过以下解决方案解决了这个问题:

在服务器端运行一个任务并在其中处理一些事件:

public FindStudies_DTO_OUT FindStudies(FindStudies_DTO_IN findStudies_DTO_IN)
    
        var token = Token;
        List<Study_C> ret = new List<Study_C>();
        _dispatcherCallBack = OperationContext.Current.GetCallbackChannel<IDispatcherCallBack>();
        AnnuncedFindStudies += (s, e) =>
        
            _dispatcherCallBack.OnAnnounceFindStudies(e);
        ;
        AnnuncedSPError += (s, e) =>
        
            _dispatcherCallBack.OnAnnunceSPError(e);
        ;
        AnnuncedComplete += (s, e) =>
        
            _dispatcherCallBack.OnAnnunceComplete();
        ;
        Task.Run(() =>
        
            //Blah blah
            if (proxyGetError)
                OnAnnuncedSPError(new SPError_DTO()
                
                    ServicePointName = sp.Name,
                    ErrorMessage = "Ping failed for " + sp.Name
                );
            var result = new List<Study_C>();//Filled
            lock (ret)
            
                OnAnnounceFindStudies(new FindStudies_DTO()
                
                    ServicePointName = sp.Name,
                    Studies = result
                ); 
            
            //blah blah
            OnAnnounceComplete();
        );
        return new FindStudies_DTO_OUT();
    

在消费者端,任务也会在超时的情况下运行,并在AnnuncedComplete 被提升时完成:

 DispatcherClient dispatcherClient = new DispatcherClient(new DispatcherCallBack(), "dispatcherEndPoint", Token, Global.Cluster);
                    Task.Run(() =>
                    
                        studies = new List<Study_C>();
                        try
                        
                            ManualResetEvent ev = new ManualResetEvent(false);
                            dispatcherClient.AnnuncedSPError += (s, e) =>
                            
                                spErrorMessage += e.ServicePointName + "<br/>";
                            ;
                            dispatcherClient.AnnuncedFindStudies += (s, e) =>
                             
                                lock (studies)
                                
                                    studies.AddRange(e.Studies);
                                
                            ;
                            dispatcherClient.AnnuncedComplete += (s, e) =>
                            
                                ev.Set();
                            ;
                            var rrr = dispatcherClient.FindStudies(new FindStudies_DTO_IN(startIndex, numberOfRows, col.FromText(sortColumnName), sort.FromText(sortOrder), criteria, searchMatching)).Studies;
                            ev.WaitOne();
                        
                        catch (Exception exp)
                        
                            Logging.Log(LoggingMode.Error, "Failed to Find Studies by Dispatcher, EXP:0", exp);
                        
                    ).Wait(dispatcherClient.Endpoint.Binding.ReceiveTimeout);

主要问题是findTask.Wait() 没有毫秒超时 但是通过为任务设置毫秒超时,一切都很好。

我认为在 ASP.Net 管道中,无限任务是不可接受的,因为这个原因引擎应该知道任务最终会因超时而结束......

【讨论】:

以上是关于按任务调用异步操作的主要内容,如果未能解决你的问题,请参考以下文章

获取 异步执行调用的结果

SpringBoot之@Async异步调用

任务Task系列之异步编程(async and await)

15.6.2Task使用 组合异步操作

23.FutureTask基本操作总结

SpringBoot系列——@Async优雅的异步调用