在多任务中处理异常以调用WCF操作的最佳方法

时间:2017-02-07 13:35:30

标签: c# multithreading task-parallel-library

我已经实现了一个服务名称ExamClient,其中有两个操作,一个是Ping,它返回一个基本string表示服务可用,另一个是FindStudy搜索到DB可能需要很长时间才能继续。

另一方面,我有ExamClient个魔杖的几个端点,每个端点按任务运行FindStudy所以在Dispatcher我有类似的东西:

public FindStudies_DTO_OUT FindStudies(FindStudies_DTO_IN findStudies_DTO_IN)
{
    List<Study_C> ret = new List<Study_C>(); 
    List<Task> tasks = new List<Task>();
    foreach (var sp in Cluster)
    {
        string serviceAddress = sp.GetLibraryAddress(ServiceLibrary_C.PCM) + "/Exam.svc";
        var task = Task.Run(() =>
        {
            ExamClient examClient = new ExamClient(serviceAddress.GetBinding(), new EndpointAddress(serviceAddress), Token);
            var ping = Task.Run(() =>
            {
                examClient.Ping();
            }); 
            if (!ping.Wait(examClient.Endpoint.Binding.OpenTimeout))
            {
                Logging.Log(LoggingMode.Warning, "Timeout on FindStudies for:{0}, address:{1}", sp.Name, serviceAddress);
                return new List<Study_C>(); // if return null then need to manage it on ret.AddRange(t.Result);
            }  
            return (examClient.FindStudies(findStudies_DTO_IN).Studies.Select(x =>
            {
                x.StudyInstanceUID = string.Format("{0}|{1}", sp.Name, x.StudyInstanceUID);
                x.InstitutionName = sp.Name;
                return x;
            }));
        });
        task.ContinueWith(t =>
        {
            lock (ret)
            {
                ret.AddRange(t.Result);
            }
        }, TaskContinuationOptions.OnlyOnRanToCompletion);
        task.ContinueWith(t =>
        {
            Logging.Log(LoggingMode.Error, "FindStudies failed for :{0}, address:{1}, EXP:{2}", sp.Name, serviceAddress, t.Exception.ToString());
        }, TaskContinuationOptions.OnlyOnFaulted);

        tasks.Add(task);
    } 
    try
    {
        Task.WaitAll(tasks.ToArray());
    }
    catch (AggregateException aggEx)
    {
        foreach (Exception exp in aggEx.InnerExceptions)
        {
            Logging.Log(LoggingMode.Error, "Error while FindStudies EXP:{0}", exp.ToString());
        }
    }

    return new FindStudies_DTO_OUT(ret.Sort(findStudies_DTO_IN.SortColumnName, findStudies_DTO_IN.SortOrderBy));
}

首先,我必须在每个端点运行Ping才能知道建立连接 之后FindStudy

如果Cluster中有三个结束品脱,那么六个任务将以并行模式运行,3个用于Ping,3个用于FindStudy

我觉得处理异常的代码有问题... 那么实现这种情况的最佳方法是什么?

提前感谢。

1 个答案:

答案 0 :(得分:1)

让我回答一下,简化并删除不必要的代码块。以及代码中的一些解释。

public FindStudies_DTO_OUT FindStudies(FindStudies_DTO_IN findStudies_DTO_IN)
{
    // Thread-safe collection
    var ret = new ConcurrentBag<Study_C>()

    // Loop cluster list and process each item in parallel and wait all process to finish. This handle the parallism better than task run
    Parallel.Foreach(Cluster, (sp) =>
    {
        var serviceAddress = sp.GetLibraryAddress(ServiceLibrary_C.PCM) + "/Exam.svc";

        ExamClient examClient = new ExamClient(serviceAddress.GetBinding(), new EndpointAddress(serviceAddress), Token);

        try
        {
            examClient.Ping();              
            // declare result variable type outside try catch to be visible below               
            var result = examClient.FindStudies(findStudies_DTO_IN);
        }
        catch(TimeoutException timeoutEx)
        {
            // abort examclient to dispose channel properly
            Logging.Log(LoggingMode.Warning, "Timeout on FindStudies for:{0}, address:{1}", sp.Name, serviceAddress);
        }
        catch(FaultException fault)
        {
            Logging.Log(LoggingMode.Error, "FindStudies failed for :{0}, address:{1}, EXP:{2}", sp.Name, serviceAddress, fault.Exception.ToString());
        }
        catch(Exception ex)
        {
            // anything else
        }
        // add exception type as needed for proper logging

        // use inverted if to reduce nested condition
        if( result == null )
            return null;

        var study_c = result.Studies.Select(x =>
        {
            x.StudyInstanceUID = string.Format("{0}|{1}", sp.Name, x.StudyInstanceUID);
            x.InstitutionName = sp.Name;
            return x;
        }));

        // Thread-safe collection
        ret.AddRange(study_c);      
    });

    // for sorting i guess concurrentBag has orderby; if not working convert to list
    return new FindStudies_DTO_OUT(ret.Sort(findStudies_DTO_IN.SortColumnName, findStudies_DTO_IN.SortOrderBy));
}

注意:代码尚未测试,但要点就在那里。另外我觉得task.run里面的task.run是个坏主意,不记得我读过哪篇文章(可能来自Stephen Cleary不确定)。