线程安全

1、线程安全集合

BlockingCollection:一个支持界限和阻塞的容器(线程安全集合),与队列结构相似,常用函数如下

Add :向容器中插入元素

TryTake:从容器中取出元素并删除

TryPeek:从容器中取出元素,但不删除。

CompleteAdding:告诉容器,添加元素完成。此时如果还想继续添加会发生异常。

IsCompleted:告诉消费线程,生产者线程还在继续运行中,任务还未完成。

普通用法示例:

/// <summary>
/// 线程安全集合用法
/// </summary>
public static void BC()
{
    //线程安全集合
    using (BlockingCollection<int> blocking = new BlockingCollection<int>())
    {
        int NUMITEMS = 10000;

        for (int i = 1; i < NUMITEMS; i++)
        {
            blocking.Add(i);
        }
        //完成添加
        blocking.CompleteAdding();

        int outerSum = 0;

        // 定义一个委托方法取出集合元素
        Action action = () =>
        {
            int localItem;
            int localSum = 0;

            //取出并删除元素,先进先出
            while (blocking.TryTake(out localItem))
            {
                localSum += localItem;
            }
            //两数相加替换第一个值
            Interlocked.Add(ref outerSum, localSum);
        };

        //并行3个线程执行,多个线程同时取集合的数据
        Parallel.Invoke(action, action, action);

        Console.WriteLine($"0+...{NUMITEMS-1} = {((NUMITEMS * (NUMITEMS - 1)) / 2)},输出结果:{outerSum}");
        //此集合是否已标记为已完成添加且为空
        Console.WriteLine($"线程安全集合.IsCompleted={blocking.IsCompleted}");
    }
}

 

 

 

 限制集合长度示例

/// <summary>
/// 限制集合长度
/// </summary>
public static void BCLimtLength()
{
    //限制集合长度为5个,后面进的会阻塞等集合少于5个再进来
    BlockingCollection<int> blocking = new BlockingCollection<int>(5);

    var task1= Task.Run(() =>
    {
        for (int i = 0; i < 20; i++)
        {
            blocking.Add(i);
            Console.WriteLine($"集合添加:({i})");
        }

        blocking.CompleteAdding();
        Console.WriteLine("完成添加");
    });

    // 延迟500ms执行等待先生产数据
    var task2 = Task.Delay(500).ContinueWith((t) =>
    {
        while (!blocking.IsCompleted)
        {
            var n = 0;
            if (blocking.TryTake(out n))
            {
                Console.WriteLine($"取出:({n})");
            }
        }

        Console.WriteLine("IsCompleted = true");
    });

    Task.WaitAll(task1, task2);
}

 

 

 在BlockingCollection中使用Stack(栈,先进后出)示例:

/// <summary>
/// 线程安全集合,先进后出
/// </summary>
public static void BCStack()
{
    //线程安全集合,参数表明栈标识,队列长度为5
    BlockingCollection<int> blocking = new BlockingCollection<int>(new ConcurrentStack<int>(), 5);

    var task1 = Task.Run(() =>
    {
        for (int i = 0; i < 20; i++)
        {
            blocking.Add(i);
            Console.WriteLine($"集合添加:({i})");
        }

        blocking.CompleteAdding();
        Console.WriteLine("完成添加");
    });

    // 等待先生产数据
    var task2 = Task.Delay(500).ContinueWith((t) =>
    {
        while (!blocking.IsCompleted)
        {
            var n = 0;
            if (blocking.TryTake(out n))
            {
                Console.WriteLine($"取出:({n})");
            }
        }

        Console.WriteLine("IsCompleted = true");
    });

    Task.WaitAll(task1, task2);
}

 

 

 

2、线程安全字典

ConcurrentDictionary :这个比较好理解,普通字典多线程并发时添加时会报错,而这个则是线程安全的,不会报错。

普通字典示例:

//普通字典
private static IDictionary<string, string> Dictionaries { get; set; } = new Dictionary<string, string>();
/// <summary>
/// 字典增加值
/// </summary>
public static void AddDictionaries()
{
    Stopwatch sw = new Stopwatch();
    sw.Start();
    //并发1000个线程写
    Parallel.For(0, 1000, (i) =>
    {
        var key = $"key-{i}";
        var value = $"value-{i}";

        // 不加锁会报错
       // lock (Dictionaries)
       // {
            Dictionaries.Add(key, value);
       // }
    });
    sw.Stop();
    Console.WriteLine("Dictionaries 当前数据量为: {0}", Dictionaries.Count);
    Console.WriteLine("Dictionaries 执行时间为: {0} ms", sw.ElapsedMilliseconds);
}  

不加锁时结果:

 

 加锁后:

 

 线程安全字典示例:

//线程安全字典
private static IDictionary<string, string> ConcurrentDictionaries { get; set; } = new ConcurrentDictionary<string, string>();

/ <summary>
/// 线程安全字典添加值
/// </summary>
public static void AddConcurrentDictionaries()
{
    Stopwatch sw = new Stopwatch();
    sw.Start();
    //并发1000个线程写
    Parallel.For(0, 1000, (i) =>
    {
        var key = $"key-{i}";
        var value = $"value-{i}";

        // 无须加锁
        ConcurrentDictionaries.Add(key, value);
        
    });
    sw.Stop();
    Console.WriteLine("ConcurrentDictionaries 当前数据量为: {0}", ConcurrentDictionaries.Count);
    Console.WriteLine("ConcurrentDictionaries 执行时间为: {0} ms", sw.ElapsedMilliseconds);
}

 

 可以看到,线程安全字典比普通字典性能略好,且线程安全字典无需加锁。

1

posted @ 2022-04-22 16:18  白草红叶黄花  阅读(235)  评论(0编辑  收藏  举报