【发布时间】:2017-02-24 00:25:58
【问题描述】:
我们有一个 Client 类,它扩展了一个 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 包含多个并行调用的操作。 Client不是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);
}
}
如您所见,每个操作都会调用 Proxy 和 CleanUp 操作。
我们尚未发现任何行为模式,但有时(可能每天一次)我们会在日志文件中看到此错误:
发生了一个或多个错误。InnerException: System.ObjectDisposedException: 无法访问已处置的对象。 对象名称:'System.ServiceModel.Channels.ServiceChannel'。
它不会发生在任何特定的操作上。它随时变化。 我相信代理需要在构造过程中完成,而在处置过程中需要清理,但这意味着要更改几件事,我想确定一下。
我非常感谢任何关于如何改进它的想法。
【问题讨论】:
-
为什么
_proxy是一个字段?为什么不让Proxy返回一个WebServiceClient<T>将其保存在函数的本地变量中,然后将该变量传递给Cleanup此外,您的后台工作人员除了为您的函数调用增加额外的开销之外什么也不做。如果您打算将工作放在另一个线程上,然后立即调用.Wait()以完成该线程,那么在根本不使用额外线程的情况下,您的性能总是会变差。 -
你真的需要停止在你的代码中写
catch (Exception ex)。它只是引入了错误。您应该只捕获可以从中恢复且异常的异常。阅读Eric Lippert's Vexing Exceptions。 -
为了在此处获得竞争条件,您必须在同一实例上多次调用 OperationAsync(根据您的陈述,您没有这样做),或者将 _proxy 作为静态成员(您没有显示该字段的定义)。所以我猜竞争条件实际上在您没有显示的代码中具有不同的原因(即您确实在单个实例上多次调用 OperationAsync)。
标签: c# multithreading concurrency thread-safety race-condition