如何防止这种竞争条件?
How to prevent this race condition?
我们有一个 Client class 它扩展了 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;
}
}
并且 Client 包含多个并行调用的操作。 客户端不是单例,我们每次生成一个实例。
操作如下:
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);
}
}
如您所见,每个操作都会调用 Proxy 和 CleanUp 操作。
我们尚未发现任何行为模式,但有时(可能一天一次)我们会在日志文件中看到此错误:
出现一个或多个错误.. InnerException: System.ObjectDisposedException: 无法访问已释放的对象。
对象名称:'System.ServiceModel.Channels.ServiceChannel'.
它不会发生在任何特定的操作上。它一直在变化。
我相信 Proxy 需要在构造函数期间完成,而 CleanUp 需要在处置期间完成,但这意味着要改变几件事,我想确定一下。
如果有任何关于如何改进它的想法,我将不胜感激。
由于您的原始代码正在调用 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);
}
}
我们有一个 Client class 它扩展了 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;
}
}
并且 Client 包含多个并行调用的操作。 客户端不是单例,我们每次生成一个实例。
操作如下:
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);
}
}
如您所见,每个操作都会调用 Proxy 和 CleanUp 操作。
我们尚未发现任何行为模式,但有时(可能一天一次)我们会在日志文件中看到此错误:
出现一个或多个错误.. InnerException: System.ObjectDisposedException: 无法访问已释放的对象。 对象名称:'System.ServiceModel.Channels.ServiceChannel'.
它不会发生在任何特定的操作上。它一直在变化。 我相信 Proxy 需要在构造函数期间完成,而 CleanUp 需要在处置期间完成,但这意味着要改变几件事,我想确定一下。
如果有任何关于如何改进它的想法,我将不胜感激。
由于您的原始代码正在调用 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);
}
}