C# 如果实例已被释放,则调用BeginXXX而不调用EndXXX是否安全
使用时,通常建议将每个C# 如果实例已被释放,则调用BeginXXX而不调用EndXXX是否安全,c#,.net,asynchronous,memory-leaks,idisposable,C#,.net,Asynchronous,Memory Leaks,Idisposable,使用时,通常建议将每个BeginXXX与EndXXX匹配,否则在异步操作完成之前,您可能会泄漏资源 如果类实现了IDisposable,并且实例是通过调用Dispose来处理的,那么情况仍然是这样吗 例如,如果我在UdpListener中使用UdpClient.BeginReceive: class UdpListener : IDisposable { private bool _isDisposed; private readonly IPAddress _hostIpAdd
BeginXXX
与EndXXX
匹配,否则在异步操作完成之前,您可能会泄漏资源
如果类实现了IDisposable
,并且实例是通过调用Dispose
来处理的,那么情况仍然是这样吗
例如,如果我在UdpListener
中使用UdpClient.BeginReceive
:
class UdpListener : IDisposable
{
private bool _isDisposed;
private readonly IPAddress _hostIpAddress;
private readonly int _port;
private UdpClient _udpClient;
public UdpListener(IPAddress hostIpAddress, int port)
{
_hostIpAddress = hostIpAddress;
_port = port;
}
public void Start()
{
_udpClient.Connect(_hostIpAddress, _port);
_udpClient.BeginReceive(HandleMessage, null);
}
public void Dispose()
{
if (_isDisposed)
{
throw new ObjectDisposedException("UdpListener");
}
((IDisposable) _udpClient).Dispose();
_isDisposed = true;
}
private static void HandleMessage(IAsyncResult asyncResult)
{
// handle...
}
}
我是否仍需要确保对已处置的\u UdpClient
调用UdpClient.EndReceive
(这只会导致ObjectDisposedException
)
编辑: 在所有异步操作完成之前处理
UdpClient
(以及其他IDisposable
s)作为取消或实现超时的一种方式并不少见,尤其是在超时之后。这也是推荐的
在使用异步编程模型时,通常建议将每个BeginXXX
与EndXXX
匹配,否则在异步操作仍在“运行”时,可能会泄漏保留的资源
如果类实现了IDisposable
,并且对实例调用了Dispose
,那么情况仍然如此吗
这与是否实现IDisposable
的类无关
除非您可以确保异步完成将释放与通过BeginXXX
启动的异步操作相关的任何资源,并且没有在中执行任何清理,或者由于EndXXX
调用,否则您需要确保与调用相匹配。唯一确定这一点的方法是检查特定异步操作的实现
对于您选择的示例,情况恰好是:
UDPClient
实例后调用EndXXX
将导致它直接抛出ObjectDisposedException
EndXXX
调用中或调用结果中不会释放任何资源\u udpClient
实例上调用Close
以强制I/O失败)UdpClient.BeginReceive
方法文档中的以下内容:
异步BeginReceive
操作必须通过调用EndReceive
方法来完成。通常,该方法由requestCallback委托调用
对于基础方法,请执行以下操作:
异步BeginReceive
操作必须通过调用EndReceive
方法来完成。通常,该方法由回调委托调用
要取消挂起的BeginReceive
,请调用Close
方法
即,这是“按设计”记录的行为。你可以争论设计是否很好,但是很清楚预期的取消方法是什么,以及这样做的结果是什么
对于您的特定示例(已更新以对异步结果执行一些有用的操作)和其他类似情况,以下是遵循推荐方法的实现:
public class UdpListener : IDisposable
{
private readonly IPAddress _hostIpAddress;
private readonly int _port;
private readonly Action<UdpReceiveResult> _processor;
private TaskCompletionSource<bool> _tcs = new TaskCompletionSource<bool>();
private CancellationTokenSource _tokenSource = new CancellationTokenSource();
private CancellationTokenRegistration _tokenReg;
private UdpClient _udpClient;
public UdpListener(IPAddress hostIpAddress, int port, Action<UdpReceiveResult> processor)
{
_hostIpAddress = hostIpAddress;
_port = port;
_processor = processor;
}
public Task ReceiveAsync()
{
// note: there is a race condition here in case of concurrent calls
if (_tokenSource != null && _udpClient == null)
{
try
{
_udpClient = new UdpClient();
_udpClient.Connect(_hostIpAddress, _port);
_tokenReg = _tokenSource.Token.Register(() => _udpClient.Close());
BeginReceive();
}
catch (Exception ex)
{
_tcs.SetException(ex);
throw;
}
}
return _tcs.Task;
}
public void Stop()
{
var cts = Interlocked.Exchange(ref _tokenSource, null);
if (cts != null)
{
cts.Cancel();
if (_tcs != null && _udpClient != null)
_tcs.Task.Wait();
_tokenReg.Dispose();
cts.Dispose();
}
}
public void Dispose()
{
Stop();
if (_udpClient != null)
{
((IDisposable)_udpClient).Dispose();
_udpClient = null;
}
GC.SuppressFinalize(this);
}
private void BeginReceive()
{
var iar = _udpClient.BeginReceive(HandleMessage, null);
if (iar.CompletedSynchronously)
HandleMessage(iar); // if "always" completed sync => stack overflow
}
private void HandleMessage(IAsyncResult iar)
{
try
{
IPEndPoint remoteEP = null;
Byte[] buffer = _udpClient.EndReceive(iar, ref remoteEP);
_processor(new UdpReceiveResult(buffer, remoteEP));
BeginReceive(); // do the next one
}
catch (ObjectDisposedException)
{
// we were canceled, i.e. completed normally
_tcs.SetResult(true);
}
catch (Exception ex)
{
// we failed.
_tcs.TrySetException(ex);
}
}
}
公共类UdpListener:IDisposable
{
专用只读IP地址\u主机IP地址;
专用只读int_端口;
专用只读操作处理器;
私有TaskCompletionSource_tcs=新TaskCompletionSource();
private CancellationTokenSource _tokenSource=新的CancellationTokenSource();
私有取消令牌注册\u令牌注册;
私有UDP客户_UDP客户;
公共UdpListener(IPAddress主机IPAddress、int端口、操作处理器)
{
_hostIpAddress=hostIpAddress;
_端口=端口;
_处理器=处理器;
}
公共任务ReceiveAsync()
{
//注意:在并发调用的情况下,这里有一个竞争条件
if(_tokenSource!=null&&u udpClient==null)
{
尝试
{
_udpClient=新的udpClient();
_udpClient.Connect(_主机地址,_端口);
_tokenReg=\u tokenSource.Token.Register(()=>\u udpClient.Close());
BeginReceive();
}
捕获(例外情况除外)
{
_tcs.SetException(ex);
投掷;
}
}
返回_tcs.Task;
}
公共停车场()
{
var cts=Interlocked.Exchange(ref\u tokenSource,null);
如果(cts!=null)
{
cts.Cancel();
if(_tcs!=null&&u udpClient!=null)
_Task.Wait();
_tokenReg.Dispose();
cts.Dispose();
}
}
公共空间处置()
{
停止();
如果(_udpClient!=null)
{
((IDisposable)u udpClient.Dispose();
_udpClient=null;
}
总干事(本);
}
私人无效开始接收()
{
var iar=\u udpClient.Begin