如何防止这种竞争状况?

时间:2017-02-24 00:25:59

标签: c# multithreading concurrency thread-safety race-condition

我们有一个客户端类,它扩展了 BaseClass

BaseClass 有以下方法:

    protected void Proxy()
    {
        Error = null;
        _proxy = new WebServiceClient<T>(_fullURL);
        Error = _proxy.Error;
    }

    protected virtual void Cleanup()
    {
        if (_proxy != null)
        {
            _proxy.Dispose();
            _proxy = null;
        }
    }

客户端包含多个并行调用的操作。 客户端不是Singleton,我们每次都会生成一个实例。

操作如下:

public void OperationAsync(Action<BaseResult> callback)
{

    TaskCompletionSource<String> taskSrc = new TaskCompletionSource<String>();
    Task<String> tsk = taskSrc.Task;
    try
    {
        Proxy();

        ThreadPool.QueueUserWorkItem(t =>
        {
            try
            {
                String result = _proxy.Channel.ExecuteOperation(SecurityToken());
                taskSrc.SetResult(result);
            }
            catch (Exception ex)
            {
                taskSrc.SetException(ex);
            }
        });

        tsk.Wait();

        BaseResult r = new BaseResult();
        r.Value = tsk.Result;
        r.Error = tsk.Exception;
        Cleanup();

        if (callback != null)
        {
            callback(r);
        }

    }
    catch (Exception ex)
    {
        FileManager.Log(ex);
    }
}

如您所见,每项操作都会调用代理 CleanUp 操作。

我们还没有发现任何行为模式,但有时(可能每天一次)我们在日志文件中看到此错误:

发生了一个或多个错误.BurnerException:System.ObjectDisposedException:无法访问已处置的对象。 对象名称:'System.ServiceModel.Channels.ServiceChannel'。

任何具体操作都不会发生。它总是变化的。 我相信代理需要在构造函数和清理期间完成处理,但它意味着要改变一些事情,我想确定。

我真的很感激如何改进它。

1 个答案:

答案 0 :(得分:1)

由于您的原始代码正在调用tsk.Wait();,因此您在后台线程上运行代理代码时阻止了调用线程。没有任何好处,可能会增加这样的开销。

所以,这是如何防止竞争条件:

public void OperationAsync(Action<BaseResult> callback)
{
    try
    {
        var r = new BaseResult();
        using (var proxy = new WebServiceClient<T>(_fullURL))
        {
            try
            {
                r.Value = proxy.Channel.ExecuteOperation(SecurityToken());
            }
            catch (Exception ex)
            {
                r.Error = ex;
            }
        }
        if (callback != null)
        {
            callback(r);
        }
    }
    catch (Exception ex)
    {
        FileManager.Log(ex);
    }
}