• 并发技术中同步


      如果程序中用到了并发技术,一段代码需要修改数据,同时其他代码需要访问同一数据。

      同步的类型:a.通信  b.数据保护。

      如果以下三个条件都满足,就需要使用同步来保护数据。

    • 多段代码正在并发运行;
    • 这几段代码在访问(读或写)同一个数据;
    • 至少有一段代码在修改数据。

    1、阻塞锁    lock

      多个线程需要安全的读写共享数据。

      一个线程进入锁后,在锁被释放之前,其他线程是无法进入的。

      锁的使用,有四条重要的规则:

    • 限制锁的作用范围
    • 文档中写清锁保护的内容
    • 锁范围内的代码尽量少
    • 在控制锁的时候,绝不运行随意的代码

      首先,要尽量限制锁的作用范围。应该把 lock 语句使用的对象设为私有成员,并且永远不
    要暴露给非本类的方法。每个类型通常最多只有一个锁。如果一个类型有多个锁,可考虑通
    过重构把它分拆成多个独立的类型。可以锁定任何引用类型,但是我建议为 lock 语句定义
    一个专用的成员,就像最后的例子那样。尤其是千万不要用 lock(this),也不要锁定 Type
    或 string 类型的实例。因为这些对象是可以被其他代码访问的,这样锁定会产生死锁。
      第二,要在文档中描述锁定的内容。这种做法在最初编写代码时很容易被忽略,但是在代
    码变得复杂后就会变得很重要。
      第三,在锁定时执行的代码要尽可能得少。要特别小心阻塞调用。在锁定时不要做任何阻
    塞操作。
      最后,在锁定时绝不要调用随意的代码。随意的代码包括引发事件、调用虚拟方法、调用
    委托。如果一定要运行随意的代码,就在释放锁之后运行

     

    2、异步锁  SemaphoreSlim

      多个代码需要安全读写数据,并且这些代码块可能使用await语句。同步锁的规则同样适用于异步锁。

      

     public class MyClass
        {
            /// <summary>
            /// 次锁保护_value
            /// </summary>
            private readonly SemaphoreSlim _mutx = new SemaphoreSlim(1);
    
            private int _value;
    
            public async Task DelayAndIncrementAsync()
            {
                await _mutx.WaitAsync();
                try
                {
                    await Task.Delay(TimeSpan.FromSeconds(_value));
                    _value = _value + 1;
                }
                finally
                {
                    _mutx.Release();
                }
            }
        }

    3、阻塞信号   ManualResetEventSlim

      需要从一个线程发送信号给另外一个线程

      

     public class MyClass
        {
            private readonly ManualResetEventSlim _resetEvent = new ManualResetEventSlim();
    
            private int _value;
    
            private int WaitForInitialization()
            {
                _resetEvent.Wait();
                return _value;
            }
    
            private void InitializeFromAnotherThread()
            {
                _value = 10;
                _resetEvent.Set();
            }
    
        }

    ManualResetEventSlim 是功能强大、通用的线程间信号,但必须合理地使用

    4、异步信号   

      需要在代码的各个部分间发送通知,并且要求接收方必须进行异步等待。

      

     public class MyClass
        {
            private readonly TaskCompletionSource<object> _initialized = new TaskCompletionSource<object>();
    
            private int _value1;
            private int _value2;
    
            public async Task<int> WaitForInitializationAsync()
            {
                await _initialized.Task;
                return _value1 + -_value2;
            }
    
            public void Initialize()
            {
                _value1 = 10;
                _value2 = 5;
                _initialized.TrySetResult(null);
            }
    
        }

      在所有情况下都可以用 TaskCompletionSource<T> 来异步地等待:本例中,通知来自于另一
    部分代码。如果只需要发送一次信号,这种方法很适合。但是如果要打开和关闭信号,这
    种方法就不大合适了.。

    5、限流

      有一段高度并发的代码,由于它的并发程度实在太高了,需要有方法对并发性进行限流。可以避免数据项占用太多的内存。

      如果发现程序的CPU或者网络连接数太多了,或者内存占用太多,就需要进行限流。

      

      数据流和并行代码都自带了对并发性限流的方法:

        

     IEnumerable<int> ParallelMultiplyBy2(IEnumerable<int> values)
            {
                return values.AsParallel()
                .WithDegreeOfParallelism(10)
                .Select(item => item * 2);
            }

      

      并发性异步代码可以使用 SemaphoreSlim 来限流

      

      async Task<string[]> DownloadUrlsAsync(IEnumerable<string> urls)
            {
                var httpClient = new HttpClient();
                var semaphore = new SemaphoreSlim(10);
                var tasks = urls.Select(async url =>
                {
                    await semaphore.WaitAsync();
                    try
                    {
                        return await httpClient.GetStringAsync(url);
                    }
                    finally
                    {
                        semaphore.Release();
                    }
                }).ToArray();
                return await Task.WhenAll(tasks);
            }

      

  • 相关阅读:
    Python Django 零基础破门而入篇(五)
    Python Django 零基础破门而入篇(四)
    Python Django 零基础破门而入篇(三)
    Python Django 零基础破门而入篇(二)
    Python Django 零基础破门而入篇(一)
    ACM/ICPC 之 计算几何入门-叉积-to left test(POJ2318-POJ2398)
    ACM/ICPC 之 机器调度-匈牙利算法解最小点覆盖集(DFS)(POJ1325)
    ACM/ICPC 之 卡卡的矩阵旅行-最小费用最大流(可做模板)(POJ3422)
    ACM/ICPC 之 伞兵-最小割转最大流(POJ3308)
    ACM/ICPC 之 最小割转网络流(POJ3469)
  • 原文地址:https://www.cnblogs.com/tangchun/p/9182712.html
Copyright © 2020-2023  润新知