8

在下面的代码中,我使用 CancellationToken 在生产者不生产时唤醒 GetConsumingEnumerable() 并且我想跳出 foreach 并退出任务。但我没有看到 IsCancellationRequested 被记录,并且我的 Task.Wait(timeOut) 等待完整的超时时间。我究竟做错了什么?

userToken.Task = Task.Factory.StartNew(state =>
{
    userToken.CancelToken = new CancellationTokenSource();

    foreach (var broadcast in userToken.BroadcastQueue.GetConsumingEnumerable(userToken.CancelToken.Token))
    {
        if (userToken.CancelToken.IsCancellationRequested)
        {
            Log.Write("BroadcastQueue IsCancellationRequested");
            break;
            ...
        }
    }

    return 0;
}, "TaskSubscribe", TaskCreationOptions.LongRunning);

之后...

UserToken.CancelToken.Cancel();          
try
{
    task.Wait(timeOut);
}
catch (AggregateException ar)
{
    Log.Write("AggregateException " + ar.InnerException, MsgType.InfoMsg);
}
catch (OperationCanceledException)
{
    Log.Write("BroadcastQueue Cancelled", MsgType.InfoMsg);
}
4

2 回答 2

11

您可以使用CompleteAdding()which 表示不再向集合添加项目。如果GetConsumingEnumerable使用,foreach 将优雅地结束,因为它知道等待更多项目是没有意义的。

基本上,一旦您完成了将项目添加到BlockingCollection刚才的操作:

myBlockingCollection.CompleteAdding() 

任何使用 GetConsumingEnumerable 执行 foreach 循环的线程都将停止循环。

于 2012-09-27T11:12:23.827 回答
5

我已经创建了快速原型,它似乎对我有用。

在令牌取消请求之前注意 Thread.Sleep(1000)。您可能正在为 Token 变量创建竞争条件,因为您item.CancelToken在不同的线程中创建和访问变量。

例如,旨在取消任务的代码可能会在错误的(前一个或空的)取消令牌上调用取消。

static void Main(string[] args)
{
    CancellationTokenSource token = null;
    BlockingCollection<string> coll = new BlockingCollection<string>();
    var t = Task.Factory.StartNew(state =>
    {
        token = new CancellationTokenSource();
        try
        {
            foreach (var broadcast in coll.GetConsumingEnumerable(token.Token))
            {
                if (token.IsCancellationRequested)
                {
                    return;
                }
            }
        }
        catch (OperationCanceledException)
        {
            Console.WriteLine("Cancel");
            return;
        }
    }, "TaskSubscribe", TaskCreationOptions.LongRunning);
    Thread.Sleep(1000);
    token.Cancel();
    t.Wait();
}
于 2012-02-04T03:39:56.987 回答