我正在尝试制作一个简单的程序来学习如何使用取消令牌,但它们似乎给了我很多问题。通过调整CancelAfter
值,我绝对可以阻止任务死亡,但IsCancellationRequested
并没有要求我。由于CancelAfter
似乎正在正常执行,因此IsCancellationRequested
在100%的时间内失败让我感到非常困惑。
这是代码: (注意,这是一个精简的例子)
初始化任务的功能:
void IPCheck() {
var cToken = new CancellationTokenSource();
cToken.CancelAfter(1000);
string tempIP = "123.123.123.123";
Task.Factory.StartNew(() => PingTask(tempIP, cToken.Token), cToken.Token);
}
成功(?)取消但未调用IsCancellationRequested
的任务。
void PingTask(string addressString, CancellationToken cancelToken) {
Ping currentPing = new Ping();
PingReply replyPing;
replyPing = currentPing.Send(addressString);
string returnString = "Return String Unmodified." + Environment.NewLine;
if (replyPing.Status == IPStatus.Success) {
returnString = "Ping to " + addressString + " successful." + Environment.NewLine);
}
else if (replyPing.Status == IPStatus.TimedOut) {
returnString = "Ping to " + addressString + "timed out." + Environment.NewLine);
}
if (cancelToken.IsCancellationRequested) {
returnString = "Cancellation Requested for " + addressString + Environment.NewLine);
}
TaskHelperReturnString(returnString);
return;
}
除非成功或超时,CancelAfter
设置为10000
,returnString
始终写"Return String Unmodified."
成功的ping将立即结束。使用CancelAfter(1000)
时,没有足够的时间来超时并且不会打印任何内容,包括TaskHelperReturnString()
。
我使用的IP总是失败,所以它对测试很有用。
答案 0 :(得分:0)
在所有受支持的.NET版本(4.5.2+)中,您可以使用async/await
和SendPingAsync异步发送ping,而无需Task.Run
。即使没有它们,你也不需要取消源,因为SendAsync(IPAddress,int)接受超时参数。
您的代码可能如下所示:
async Task<string> IPCheck()
{
Ping currentPing = new Ping();
var replyPing= await currentPing.SendPingAsync(addressString,1000);
switch(replyPint.Status)
{
case IPStatus.Success:
var okString = $"Ping to {addressString} successful.\n";
SuccessActions(0, arrayIndex);
return okString;
case IPStatus.Timeout:
return $"Ping to {addressString} timed out.\n";
default:
// ????
}
}
即使你想要ping超时的更大任务超时(为什么?),你可以使用Task.Delay
和Task.WhenAny
而不是取消源。比ping超时短的任务超时没有多大意义,因为你永远不会以这种方式获得ping超时。
使用Task.Delay:
async Task<string> IPCheck()
{
Ping currentPing = new Ping();
var pingTask= currentPing.SendPingAsync(addressString,1000);
var delayTask=Task.Delay(2000);
var finished = await Task.WhenAny(delayTask,pingTask);
if (finished == delayTask)
{
return "Long timeout!"
}
var replyPing=await pingTask;
switch(replyPing.Status)
{
case IPStatus.Success:
var okString = $"Ping to {addressString} successful.\n";
SuccessActions(0, arrayIndex);
return okString;
case IPStatus.Timeout:
return $"Ping to {addressString} timed out.\n";
default:
// ????
}
}
<强>更新强>
原始代码无法正常工作,因为任务永远不会等待。定义取消源的方法立即退出,强制在源上进行垃圾收集。即使将令牌传递给任务,源也不会保留。 令牌是。由于源不再存在,因此无法通知它生成的任何令牌。
要使原始代码与令牌源一起使用,必须等待任务本身,或者取消源应该具有比方法更大的范围,即它应该是一个字段。
为了做一个正确的例子,假设我们在响应之前需要 5 ping 例如:
更新2018-11-06 添加了CancellationTokenSource
的正确处置,以避免充斥定时器队列。 Explanation here
static async Task<string> IPCheck(string address)
{
using (var cts = new CancellationTokenSource(2000))
{
var result = await SendPings(address, 5, cts.Token);
return result;
}
}
static async Task<string> SendPings(string address, int count, CancellationToken token)
{
var responses = new List<PingReply>(5);
Ping currentPing = new Ping();
for (int i = 0; i < count; i++)
{
if (token.IsCancellationRequested)
{
return "Cancelled!";
}
var replyPing = await currentPing.SendPingAsync(address, 1000);
switch (replyPing.Status)
{
case IPStatus.Success:
responses.Add(replyPing);
break;
case IPStatus.TimedOut:
responses.Add(replyPing);
Console.WriteLine($"Ping to {address} timed out.\n");
break;
default:
responses.Add(replyPing);
break;
}
}
var avgTime = responses.Average(resp => resp.RoundtripTime);
return $"All pings returned. Average roundtrip {avgTime}";
}
在这种情况下,IPCheck
方法不会阻止,但是在所有5次ping都在2秒内完成或超时(ping或整体)发生之前,它都不会完成。
如果ping时间过长而且总超时太短,则IsCancellationRequested
变为true并返回Cancelled
。