异步编程

 

1  原理

注意事项

在使用asyncawait时,确保不要在async方法中直接抛出异常或者赋值给ref或out类型的参数。以下是一些最佳实践:

  • 使用try-catch块来处理异步方法的异常。
  • 不要在async方法中使用:this,因为当前线程可能已经被另一个线程所占用。
  • 不要在await表达式之前或之后返回任何值或属性。
  • 避免阻塞调用:不要在async方法中使用Wait()或Result属性,这会导致死锁或降低性能。
    异常处理:async方法中的异常需要在调用者中捕获和处理,通常在await语句之后使用try-catch块。
    性能考虑:异步编程对于I/O密集型和长时间运行操作非常有益,但对于CPU密集型任务,同步编程可能更高效。
    通过使用async和await,你可以构建响应迅速、性能高效的C#应用程序,特别是当处理网络请求、文件I/O或其他需要等待的外部资源时。

 

 

 

 

唯一可以在异步中使用的同步机制就是信号量,控制并发数量

异步任务取消

 

 

 1  第一种取消通过 Whenall 的方式取消,只要有一个任务结束任务就全部结束

 2 第二种 通过 传递构造函数取消,超过这个时间就取消任务

 第三种 通过设置 CancelAfter的时间来设置取消任务

 自定义方法的取消,不要thread.sleep

 取消任务后的善后方法 Register

 的意义在哪里了,其实只是在进入方法之前判断任务是否取消了,取消了就不执行

  异步任务超时

1 线程的超时处理

 2  异步中的超时处理,通过whenAll  ,先返回的不是我们的任务就是超时

 可以将方法写出一个异步超时扩展方法,方便简写代码,这个代码并没有对task进行取消,实际情况还需要对task进行取消

 然后用这个方法

 Net 6 有自带的超时方法 WaitAsync

 

管道 Channel,异步不阻塞 生产消费模型

同步方法调用异步方法,比如 构造函数中调用异步方法

方法1 

改成扩展方法

 方法2 使用 ContinueWith

异步方法中使用同步机制和信号量

 这种方法是不行的,lock 表示线程1 进,线程1 出,但是 遇到  await  可能是线程1 进  线程2 出,所以这就不能用lock

 1  用第三方类库  不推荐

 

  2 原生 semaphoreslim  信号量

 

异步方法中更新进度

1普通的更新进度条

 2  Progress

 优化自定义进度

 调用

 

 

 

2  运用

    2.1   BlockingCollection  阻塞集合  net4.5  netCore

BlockingCollection 自带阻塞的集合,从而不用手动的去休眠线程
Take 自带阻塞功能,所以不用去手动休眠

    //第一个参数表示内部用ConcurrentQueue 作为存储数据
        //第二个数据表示只能存放10个
        public BlockingCollection<C_Message> mQue = new BlockingCollection<C_Message>(new ConcurrentQueue<C_Message>(),50);
        public void Test()
        {
            var sendThread = new Thread(Send);
            sendThread.IsBackground=true;
            var reviceThread = new Thread(Revice);
            reviceThread.IsBackground =true;    
            sendThread.Start(1);
            reviceThread.Start(2);
            //等sendThread 接收完成
            sendThread.Join();
            ////中断线程
            //reviceThread.Interrupt();
            //reviceThread.Join();
        }
        private void Send(object obj)
        {
            int id = (int)obj;
            for (int i = 0; i < 20; i++)
            {
                mQue.Add(new C_Message() { ID = i });
                Debug.WriteLine($"生产=" + i);
                Thread.Sleep(100);
            }
            //CompleteAdding()方法用于指示不再有元素会被添加到队列中,这有助于消费者知道何时停止等待新的元素。
            //这在处理有界队列时尤为重要,因为它可以防止消费者无限期地阻塞。
            //mQue.CompleteAdding(); 

        }
        private void Revice(object obj)
        {
            int id = (int)obj;
            try
            {
                while (true) //!mQue.IsCompleted
                {
                    //Take  内部自带信号量,如果没有拿到数据,就会一直等待阻塞在这里(类似 await),一旦队列有数据就会马上拿到数据,这不是轮询是信号量
                    //如果有数据就会走下一步
                    var model = mQue.Take();
                    Debug.WriteLine($"消费=" + model.ID);
                    //Thread.Sleep(1);   
                }
            }
            catch (ThreadInterruptedException )
            {
                Debug.WriteLine($"线程中断=" + id);
            }
        }
    }
    public class C_Message
    {
        public int ID { get; set; }

    }

 

2.2   Channel Net6

为了解决异步编程的BlockingCollection 阻塞问题 所以出了这个不阻塞集合

    //创建一个没有边界的Channel  内部使用的是 ConcurrentQueue
      public  Channel<C_Message> mChanel = Channel.CreateUnbounded<C_Message>();
        //CancellationTokenSource cts = new CancellationTokenSource();
        public async void Test()
        {
            var sendThread = Send(mChanel.Writer,1);
            var reviceThread = ReviceSsync(mChanel.Reader,1);
            await sendThread;
            //mChanel.Writer.Complete();
            await Task.Delay(1000);
        }
        async Task  Send(ChannelWriter<C_Message> writer,int id)
        {
            for (int i = 0; i < 20; i++)
            {
               await writer.WriteAsync(new C_Message() { ID = i });
                Debug.WriteLine($"生产=" + i);
                await Task.Delay(100);
            }
            //CompleteAdding()方法用于指示不再有元素会被添加到队列中,这有助于消费者知道何时停止等待新的元素。
            //这在处理有界队列时尤为重要,因为它可以防止消费者无限期地阻塞。
            //mQue.CompleteAdding(); 

        }
        async  Task ReviceSsync(ChannelReader<C_Message> reader, int  id)
        {
            //int id = (int)obj;
            try
            {
                //!reader.Completion.IsCompleted
                while (true)
               {
                    //内部维持一个信号量,当没有数据的时候就会一直等待,当获取到数据的时候就会继续
                    var model = await reader.ReadAsync();
                    Debug.WriteLine($"消费=" + model.ID);
                }
            }
            catch (OperationCanceledException  canExe)
            {
                Debug.WriteLine($"task 取消" + canExe);
            }
        }
    }
    public class C_Message
    {
        public int ID { get; set; }

    }

  

 

posted @ 2024-07-16 14:52  陌念  阅读(13)  评论(0)    收藏  举报