Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/328.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 从生产者/消费者队列中删除已取消的任务_C#_Async Await_Producer Consumer - Fatal编程技术网

C# 从生产者/消费者队列中删除已取消的任务

C# 从生产者/消费者队列中删除已取消的任务,c#,async-await,producer-consumer,C#,Async Await,Producer Consumer,我想使用异步生产者/消费者队列(AsyncEx-lib)通过总线一次发送一条消息。现在,我只是通过异步阻塞来实现这一点。工作正常,但我无法控制队列:( 所以我提出了以下解决方案,问题是取消的任务没有从队列中移除。如果我将队列限制为10(因为每个消息发送需要1s,最大队列时间应该是10s左右)队列已经包含8个等待任务和2个取消的任务,下一个排队的任务将抛出InvalidOperationException,尽管这两个取消的任务不会被发送 也许有更好的方法:D class Program {

我想使用异步生产者/消费者队列(AsyncEx-lib)通过总线一次发送一条消息。现在,我只是通过异步阻塞来实现这一点。工作正常,但我无法控制队列:(

所以我提出了以下解决方案,问题是取消的任务没有从队列中移除。如果我将队列限制为10(因为每个消息发送需要1s,最大队列时间应该是10s左右)队列已经包含8个等待任务和2个取消的任务,下一个排队的任务将抛出InvalidOperationException,尽管这两个取消的任务不会被发送

也许有更好的方法:D

    class Program
{
    static AsyncProducerConsumerQueue<Tuple<string, TaskCompletionSource>> s_Queue =
        new AsyncProducerConsumerQueue<Tuple<string, TaskCompletionSource>>();

    static void Main()
    {
        StartAsync().Wait();
    }

    static async Task StartAsync()
    {
        var sendingTask = StartSendingAsync();
        var tasks = new List<Task>();

        using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(8)))
        {
            for (var i = 0; i < 10; i++)
            {
                tasks.Add(EnqueueMessageAsync("Message " + i, cts.Token));
            }

            try
            {
                await Task.WhenAll(tasks);
                Console.WriteLine("All messages sent.");
            }
            catch (TaskCanceledException)
            {
                Console.WriteLine("At least one task was canceled.");
            }                
        }

        s_Queue.CompleteAdding();
        await sendingTask;
        s_Queue.Dispose();
        Console.WriteLine("Queue completed.");

        Console.WriteLine("Press any key to continue...");
        Console.ReadKey();
    }

    static async Task EnqueueMessageAsync(string message, CancellationToken token)
    {

        var tcs = new TaskCompletionSource();
        using (token.Register(() => tcs.TrySetCanceled()))
        {
            await s_Queue.EnqueueAsync(new Tuple<string, TaskCompletionSource>(message, tcs));
            Console.WriteLine("Thread '{0}' - {1}: {2} queued.", Thread.CurrentThread.ManagedThreadId, DateTime.Now.TimeOfDay, message);
            await tcs.Task;
        }
    }

    static async Task SendMessageAsync(string message)
    {
        await Task.Delay(TimeSpan.FromSeconds(1));
        Console.WriteLine("Thread '{0}' - {1}: {2} sent.", Thread.CurrentThread.ManagedThreadId, DateTime.Now.TimeOfDay, message);
    }

    static async Task StartSendingAsync()
    {
        while (await s_Queue.OutputAvailableAsync())
        {
            var t = await s_Queue.DequeueAsync();
            if (t.Item2.Task.IsCanceled || t.Item2.Task.IsFaulted) continue;

            await SendMessageAsync(t.Item1);
            t.Item2.TrySetResult();
        }
    }
}
类程序
{
静态AsyncProducerConsumerQueue s\u队列=
新建AsyncProducerConsumerQueue();
静态void Main()
{
StartAsync().Wait();
}
静态异步任务StartAsync()
{
var sendingTask=StartSendingAsync();
var tasks=新列表();
使用(var cts=new CancellationTokenSource(TimeSpan.FromSeconds(8)))
{
对于(变量i=0;i<10;i++)
{
添加(EnqueueMessageAsync(“Message”+i,cts.Token));
}
尝试
{
等待任务。何时(任务);
Console.WriteLine(“已发送的所有消息”);
}
捕获(TaskCanceledException)
{
Console.WriteLine(“至少有一个任务被取消。”);
}                
}
s_Queue.CompleteAdding();
等待发送任务;
s_Queue.Dispose();
Console.WriteLine(“队列已完成”);
Console.WriteLine(“按任意键继续…”);
Console.ReadKey();
}
静态异步任务EnqueueMessageAsync(字符串消息、CancellationToken令牌)
{
var tcs=new TaskCompletionSource();
使用(token.Register(()=>tcs.TrySetCanceled())
{
等待s_Queue.EnqueueAsync(新元组(消息,tcs));
WriteLine(“线程'{0}'-{1}:{2}排队。”,Thread.CurrentThread.ManagedThreadId,DateTime.Now.TimeOfDay,message);
等待tcs任务;
}
}
静态异步任务SendMessageAsync(字符串消息)
{
等待任务延迟(时间跨度从秒(1));
WriteLine(“线程{0}-{1}:{2}已发送。”,Thread.CurrentThread.ManagedThreadId,DateTime.Now.TimeOfDay,message);
}
静态异步任务StartSendingSync()
{
while(等待s_Queue.OutputAvailableAsync())
{
var t=wait s_Queue.DequeueAsync();
如果(t.Item2.Task.IsCanceled | | t.Item2.Task.IsFaulted)继续;
等待SendMessageAsync(t.Item1);
t、 第2项TrySetResult();
}
}
}
编辑1:

正如svik所指出的,只有当队列已经完成时才会抛出InvalidOperationException。因此,此解决方案甚至无法解决我最初遇到的等待任务的非托管“队列”问题。例如,如果有10次以上的呼叫/10次,我会得到一个完整的队列和一个额外的非托管“队列”许多等待的任务,比如我的异步阻塞方法(AsyncMonitor)。我想我必须想出一些其他的解决方案,然后

编辑2:

我有N个不同的消息生产者(我不知道有多少,因为这不是我的代码),只有一个消费者通过总线发送消息并检查它们是否正确发送(不是真正的字符串消息)

以下代码模拟代码应该中断的情况(队列大小为10):

  • 将10条消息排队(超时5秒)
  • 等待5秒(消息0-4已发送,消息5-9已取消)
  • 将11条新消息排队(不超时)
  • 消息10-19应排队,因为队列仅包含已取消的消息
  • 消息20应引发异常(例如QueueOverflowException),因为队列已满,这将由生产者代码处理或不处理
  • 制作人:

    using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)))
    {
        for (var i = 0; i < 10; i++) { tasks.Add(EnqueueMessageAsync("Message " + i, cts.Token)); }
        await Task.Delay(TimeSpan.FromSeconds(5));
        for (var i = 10; i < 21; i++) { tasks.Add(EnqueueMessageAsync("Message " + i, default(CancellationToken))); }
    
        try
        {
            await Task.WhenAll(tasks);
            Console.WriteLine("All messages sent.");
        }
        catch (TaskCanceledException)
        {
            Console.WriteLine("At least one task was canceled.");
            Console.WriteLine("Press any key to complete queue...");
            Console.ReadKey();
        }
    }
    
    使用(var cts=new CancellationTokenSource(TimeSpan.FromSeconds(5)))
    {
    对于(var i=0;i<10;i++){tasks.Add(EnqueueMessageAsync(“Message”+i,cts.Token));}
    等待任务延迟(时间跨度从秒(5));
    对于(var i=10;i<21;i++){tasks.Add(EnqueueMessageAsync(“Message”+i,default(CancellationToken));}
    尝试
    {
    等待任务。何时(任务);
    Console.WriteLine(“已发送的所有消息”);
    }
    捕获(TaskCanceledException)
    {
    Console.WriteLine(“至少有一个任务被取消。”);
    Console.WriteLine(“按任意键完成队列…”);
    Console.ReadKey();
    }
    }
    
    目标是,我想完全控制所有应该发送的消息,但在我之前发布的代码中并非如此,因为我只能控制队列中的消息,而不能控制等待排队的消息(可能有10000条消息在异步等待排队,我不知道=>生产者代码是否会像预期的那样工作,因为发送所有正在等待的消息将花费永远的时间…)


    我希望这能让我更清楚地知道我想要实现什么;)

    我不确定回答我自己的问题是否合适,所以我不会把它标记为答案,也许有人会想出一个更好的解决方案:p

    首先,这里是生产商代码:

    static async Task StartAsync()
    {
        using (var queue = new SendMessageQueue(10, new SendMessageService()))
        using (var timeoutTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(4.5)))
        {
            var tasks = new List<Task>();
    
            for (var i = 0; i < 10; i++)
            {
                tasks.Add(queue.SendAsync(i.ToString(), timeoutTokenSource.Token));
            }
            await Task.Delay(TimeSpan.FromSeconds(4.5));
            for (var i = 10; i < 25; i++)
            {
                tasks.Add(queue.SendAsync(i.ToString(), default(CancellationToken)));
            }
    
            await queue.CompleteSendingAsync();
    
            for (var i = 0; i < tasks.Count; i++ )
            {
                try
                {
                    await tasks[i];
                    Console.WriteLine("Message '{0}' send.", i);
                }
                catch (TaskCanceledException)
                {
                    Console.WriteLine("Message '{0}' canceled.", i);
                }
                catch (QueueOverflowException ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
        }
        Console.WriteLine("Press any key to continue...");
        Console.ReadKey();
    }
    
    静态异步任务StartAsync()
    {
    使用(var queue=new SendMessageQueue(10,new SendMessageService()))
    使用(var timeoutTokenSource=new CancellationTokenSource(TimeSpan.FromSeconds(4.5)))
    {
    var tasks=新列表();
    对于(变量i=0;i<10;i++)
    {
    添加(queue.sendsync(i.ToString(),timeoutTokenSource.Token));
    }
    等待任务延迟(时间跨度从秒(4.5));
    对于(变量i=10;i<25;i++)
    {
    tasks.Add(queue.SendAsync(i.ToSt
    
    class SendMessageQueue : IDisposable
    {
        private bool m_Disposed;
        private bool m_CompleteSending;
        private Task m_SendingTask;
        private AsyncMonitor m_Monitor;
        private List<MessageTaskCompletionSource> m_MessageCollection;
        private ISendMessageService m_SendMessageService;
    
        public int Capacity { get; private set; }
    
    
        public SendMessageQueue(int capacity, ISendMessageService service)
        {
            Capacity = capacity;
            m_Monitor = new AsyncMonitor();
            m_MessageCollection = new List<MessageTaskCompletionSource>();
            m_SendMessageService = service;
            m_SendingTask = StartSendingAsync();
        }
    
        public async Task<bool> SendAsync(string message, CancellationToken token)
        {
            if (m_Disposed) { throw new ObjectDisposedException(GetType().Name); }
            if (message == null) { throw new ArgumentNullException("message"); }
    
            using (var messageTcs = new MessageTaskCompletionSource(message, token))
            {
                await AddAsync(messageTcs);
                return await messageTcs.Task;
            }
        }
    
        public async Task CompleteSendingAsync()
        {
            if (m_Disposed) { throw new ObjectDisposedException(GetType().Name); }
    
            using (m_Monitor.Enter())
            {
                m_CompleteSending = true;
            }
            await m_SendingTask;
        }
    
        private async Task AddAsync(MessageTaskCompletionSource message)
        {
            using (await m_Monitor.EnterAsync(message.Token))
            {
                if (m_CompleteSending) { throw new InvalidOperationException("Queue already completed."); }
                if (Capacity < m_MessageCollection.Count)
                {
                    m_MessageCollection.RemoveAll(item => item.IsCanceled);
                    if (Capacity < m_MessageCollection.Count)
                    {
                        throw new QueueOverflowException(string.Format("Queue overflow; '{0}' couldn't be enqueued.", message.Message));
                    }
                }
                m_MessageCollection.Add(message);
            }
            m_Monitor.Pulse(); // signal new message
            Console.WriteLine("Thread '{0}' - {1}: '{2}' enqueued.", Thread.CurrentThread.ManagedThreadId, DateTime.Now.TimeOfDay, message.Message);
        }
    
        private async Task<MessageTaskCompletionSource> TakeAsync()
        {
            using (await m_Monitor.EnterAsync())
            {
                var message = m_MessageCollection.ElementAt(0);
                m_MessageCollection.RemoveAt(0);
                return message;
            }
        }
    
        private async Task<bool> OutputAvailableAsync()
        {
            using (await m_Monitor.EnterAsync())
            {
                if (m_MessageCollection.Count > 0) { return true; }
                else if (m_CompleteSending) { return false; }
    
                await m_Monitor.WaitAsync();
                return true;
            }
        }
    
        private async Task StartSendingAsync()
        {
            while (await OutputAvailableAsync())
            {
                var message = await TakeAsync();
                if (message.IsCanceled) continue;
                try
                {
                    var result = await m_SendMessageService.SendMessageAsync(message.Message, message.Token);
                    message.TrySetResult(result);
                }
                catch (TaskCanceledException) { message.TrySetCanceled(); }
            }
        }
    
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    
        protected void Dispose(bool disposing)
        {
            if (m_Disposed) return;
            if (disposing)
            {
                if (m_MessageCollection != null)
                {
                    var tmp = m_MessageCollection;
                    m_MessageCollection = null;
                    tmp.ForEach(item => item.Dispose());
                    tmp.Clear();
                }
            }
            m_Disposed = true;
        }
    
        #region MessageTaskCompletionSource Class
    
        class MessageTaskCompletionSource : TaskCompletionSource<bool>, IDisposable
        {
            private bool m_Disposed;
            private IDisposable m_CancellationTokenRegistration;
    
            public string Message { get; private set; }
            public CancellationToken Token { get; private set; }
            public bool IsCompleted { get { return Task.IsCompleted; } }
            public bool IsCanceled { get { return Task.IsCanceled; } }
            public bool IsFaulted { get { return Task.IsFaulted; } }
    
    
            public MessageTaskCompletionSource(string message, CancellationToken token)
            {
                m_CancellationTokenRegistration = token.Register(() => TrySetCanceled());
                Message = message;
                Token = token;
            }
    
            public void Dispose()
            {
                Dispose(true);
                GC.SuppressFinalize(this);
            }
    
            protected void Dispose(bool disposing)
            {
                if (m_Disposed) return;
                if (disposing)
                {
                    TrySetException(new ObjectDisposedException(GetType().Name));
    
                    if (m_CancellationTokenRegistration != null)
                    {
                        var tmp = m_CancellationTokenRegistration;
                        m_CancellationTokenRegistration = null;
                        tmp.Dispose();
                    }
                }
                m_Disposed = true;
            }
        }
    
        #endregion
    }