C# 中的线程安全集合类

2019-12-10 03:44栏目:编程
TAG:

1、IProducerConsumerCollection (线程安全接口卡塔尔(قطر‎
  此接口的装有达成必需都启用此接口的兼具成员,若要从几个线程同一时间使用。

Thread Local Storage: Thread-Relative Static Fields and Data Slots



文章摘自msdn library官方文书档案

能够动用托管线程本地存款和储蓄区 (TLS卡塔尔 存款和储蓄某一线程和采纳程序域所只有的数目。 .NET Framework 提供了二种接纳托管 TLS 的秘诀:线程相关的静态字段和数据槽。

  • 若果您能够在编写翻译时预料到您的熨帖要求,请使用线程相关的静态字段(在 Visual Basic 中为线程相关的 bob体育平台,Shared 字段)。 线程相关的静态字段可提供最棒品质。 它们还富有编写翻译时类型检查的亮点。

  • 假如必须要在运行时发掘你的实在必要,请使用数据槽。 数据槽比线程相关的静态字段慢一些且更为费时使用,而且数据存款和储蓄为 Object.aspx卡塔尔(قطر‎类型,因而必须将其挟持调换为不易的品类才干动用。

在非托管 C++ 中,能够选取 TlsAlloc 来动态分配槽,使用 __declspec(thread) 来注明变量应在线程相关的存款和储蓄区中开展分配。 线程相关的静态字段和数据槽提供了此行为的托管版本。

在 .NET Framework 4中,能够行使 System.Threading.ThreadLocal<T>.aspx卡塔尔(英语:State of Qatar)类创制线程当地对象,在率先次利用该目的时它将惰式伊始化。 有关详细音信,请参阅延迟初阶化.aspx)。

托管 TLS 中数量的唯生机勃勃性

 

无论使用线程相关的静态字段依旧选取数据槽,托管 TLS 中的数据都以线程和选用程序域组合所独有的。

  • 在采纳程序域内部,二个线程无法改改另贰个线程中的数据,尽管那四个线程使用同三个字段或槽时也不能够。

  • 当线程从三个利用程序域中访问同二个字段或槽时,会在各种应用程序域中保护二个独自的值。

举例说,假若某些线程设置线程相关的静态字段的值,接着它踏向另一个施用程序域,然后找寻该字段的值,则在第三个利用程序域中检索的值将分化于第一个使用程序域中的值。 在第二个应用程序域中为该字段设置叁个新值不会耳熏目染率先个使用程序域中该字段的值。

长期以来,当有些线程获取七个分裂应用程序域中的同一命名数据槽时,第二个应用程序域中的数据将始终与第一个利用程序域中的数据毫无干系。

bob体育app,线程相关的静态字段

 

若果你明白有些数码连接有些线程和采纳程序域组合所独有的,请向该静态字段应用 ThreadStaticAttribute.aspx卡塔尔(قطر‎天性。 与利用别的其余静态字段相符使用该字段。 该字段中的数据是各种使用它的线程所独有的。

线程相关的静态字段的天性非凡数据槽,何况有着编写翻译时类型检查的帮助和益处。

请介怀,任何类布局函数代码都就要拜谒该字段的第二个上下文中的第一个线程上运行。 在同等应用程序域内的有着别的线程或左右文中,即便字段是引用类型,它们将被初叶化为 null(在 Visual Basic 中为 Nothing);假使字段是值类型,它们将被起首化为它们的默许值。 因而,您不应注重于类构造函数来伊始化线程相关的静态字段。 而应防止领头化线程相关的静态字段并假定它们初阶化为 null (Nothing卡塔尔(قطر‎ 或它们的暗许值。

数据槽

 

.NET Framework 提供了线程和行使程序域组合所独有的动态数据槽。 数据槽包罗两种等级次序:命名槽和未命名槽。 两个都是由此使用LocalDataStoreSlot.aspx卡塔尔国布局来落到实处的。

  • 若要成立命名数据槽,请使用 Thread.AllocateNamedDataSlot.aspx) 或 Thread.GetNamedDataSlot.aspx卡塔尔(英语:State of Qatar)方法。 若要博取对有些现存命名槽的引用,请将其名目传递给 GetNamedDataSlot.aspx) 方法。

  • 若要成立未命名数据槽,请使用 Thread.AllocateDataSlot.aspx) 方法。

对于命名槽和未命名槽,请使用 Thread.SetData.aspx) 和 Thread.GetData.aspx卡塔尔方法设置和检索槽中的新闻。 这几个都以静态方法,它们一直作用于当下正在进行它们的线程的数额。

命名槽或者很方便,因为你能够在要求它时经过将其名目传递给 GetNamedDataSlot.aspx卡塔尔(英语:State of Qatar)方法来搜索该槽,并不是维护对未命名槽的引用。 可是,若是另八个组件使用相近的名目来定名其线程相关的存款和储蓄区,并且有一个线程同一时间执行来自您的零件和该零构件的代码,则那三个零构件可能会破坏相互的数据。(本方案意气风发经那七个零器件在相似应用程序域内运营,而且它们并不用于分享雷同数量。)

C# 的集纳类型中, 都有Synchronized静态方法, 和SyncRoot实例方法

bob体育平台 1bob体育平台 2

对于ArrayList以致Hashtable 集结类来说,当必要做到线程安全的时候,最棒使用其自带的质量SyncRoot 来成功,纵然也得以选用其Synchronized(卡塔尔(英语:State of Qatar)方法来兑现,可是采用质量会越来越好。

using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;

namespace ConsoleApp1
{
    public class SafeStack<T> : IProducerConsumerCollection<T>
    {
        // Used for enforcing thread-safety
        private object m_lockObject = new object();

        // We'll use a regular old Stack for our core operations
        private Stack<T> m_sequentialStack = null;

        //
        // Constructors
        //
        public SafeStack()
        {
            m_sequentialStack = new Stack<T>();
        }

        public SafeStack(IEnumerable<T> collection)
        {
            m_sequentialStack = new Stack<T>(collection);
        }

        //
        // Safe Push/Pop support
        //
        public void Push(T item)
        {
            lock (m_lockObject) m_sequentialStack.Push(item);
        }

        public bool TryPop(out T item)
        {
            bool rval = true;
            lock (m_lockObject)
            {
                if (m_sequentialStack.Count == 0) { item = default(T); rval = false; }
                else item = m_sequentialStack.Pop();
            }
            return rval;
        }

        //
        // IProducerConsumerCollection(T) support
        //
        public bool TryTake(out T item)
        {
            return TryPop(out item);
        }

        public bool TryAdd(T item)
        {
            Push(item);
            return true; // Push doesn't fail
        }

        public T[] ToArray()
        {
            T[] rval = null;
            lock (m_lockObject) rval = m_sequentialStack.ToArray();
            return rval;
        }

        public void CopyTo(T[] array, int index)
        {
            lock (m_lockObject) m_sequentialStack.CopyTo(array, index);
        }



        //
        // Support for IEnumerable(T)
        //
        public IEnumerator<T> GetEnumerator()
        {
            // The performance here will be unfortunate for large stacks,
            // but thread-safety is effectively implemented.
            Stack<T> stackCopy = null;
            lock (m_lockObject) stackCopy = new Stack<T>(m_sequentialStack);
            return stackCopy.GetEnumerator();
        }


        //
        // Support for IEnumerable
        //
        IEnumerator IEnumerable.GetEnumerator()
        {
            return ((IEnumerable<T>)this).GetEnumerator();
        }

        // 
        // Support for ICollection
        //
        public bool IsSynchronized
        {
            get { return true; }
        }

        public object SyncRoot
        {
            get { return m_lockObject; }
        }

        public int Count
        {
            get { return m_sequentialStack.Count; }
        }

        public void CopyTo(Array array, int index)
        {
            lock (m_lockObject) ((ICollection)m_sequentialStack).CopyTo(array, index);
        }
    }
}

线程安全会集:
BlockingCollection:
二个线程安全会集类,可为任何项目标集聚提供线程安全

SafeStack

什么时候使用线程安全集合 
该作品解释了.net framework4新引入的八个特地协助三十二线程添加和删除操作而设计的会合类型。分歧于从前版本的中会集类型中的SyncRoot属性 以致 Synchronized(卡塔尔(قطر‎方法,这几个新品类应用了神速的锁定和免锁定同步机制

bob体育平台 3bob体育平台 4

ConcurrentQueue(T)
ConcurrentStack(T)
ConcurrentDictionary(TKey, TValue)
ConcurrentBag(T)
BlockingCollection(T)

using System;
using System.Collections.Concurrent;

namespace ConsoleApp1
{
    class Program
    {
        static void Main()
        {
            TestSafeStack();

            // Keep the console window open in debug mode.
            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }

        // Test our implementation of IProducerConsumerCollection(T)
        // Demonstrates:
        //      IPCC(T).TryAdd()
        //      IPCC(T).TryTake()
        //      IPCC(T).CopyTo()
        static void TestSafeStack()
        {
            SafeStack<int> stack = new SafeStack<int>();
            IProducerConsumerCollection<int> ipcc = (IProducerConsumerCollection<int>)stack;

            // Test Push()/TryAdd()
            stack.Push(10); Console.WriteLine("Pushed 10");
            ipcc.TryAdd(20); Console.WriteLine("IPCC.TryAdded 20");
            stack.Push(15); Console.WriteLine("Pushed 15");

            int[] testArray = new int[3];

            // Try CopyTo() within boundaries
            try
            {
                ipcc.CopyTo(testArray, 0);
                Console.WriteLine("CopyTo() within boundaries worked, as expected");
            }
            catch (Exception e)
            {
                Console.WriteLine("CopyTo() within boundaries unexpectedly threw an exception: {0}", e.Message);
            }

            // Try CopyTo() that overflows
            try
            {
                ipcc.CopyTo(testArray, 1);
                Console.WriteLine("CopyTo() with index overflow worked, and it SHOULD NOT HAVE");
            }
            catch (Exception e)
            {
                Console.WriteLine("CopyTo() with index overflow threw an exception, as expected: {0}", e.Message);
            }

            // Test enumeration
            Console.Write("Enumeration (should be three items): ");
            foreach (int item in stack)
                Console.Write("{0} ", item);
            Console.WriteLine("");

            // Test TryPop()
            int popped = 0;
            if (stack.TryPop(out popped))
            {
                Console.WriteLine("Successfully popped {0}", popped);
            }
            else Console.WriteLine("FAILED to pop!!");

            // Test Count
            Console.WriteLine("stack count is {0}, should be 2", stack.Count);

            // Test TryTake()
            if (ipcc.TryTake(out popped))
            {
                Console.WriteLine("Successfully IPCC-TryTaked {0}", popped);
            }
            else Console.WriteLine("FAILED to IPCC.TryTake!!");
        }
    }
}

IProducerConsumerCollection<T>
概念了操作线程安全集合的不二等秘书技,以供成品/使用者利用

Program

躬行实践请看:
IProducerConsumerCollection<T> Interface

2、ConcurrentStack类:安全货仓

合法示例给的是根据仓库的线程安全实现,他继续自该接口。然后加锁lock来促成线程安全,该接口有多个点子:

bob体育平台 5bob体育平台 6

[__DynamicallyInvokable]
public interface IProducerConsumerCollection<T> : IEnumerable<T>, ICollection, IEnumerable
{
// Methods
[__DynamicallyInvokable]
void CopyTo(T[] array, int index);
[__DynamicallyInvokable]
T[] ToArray();
[__DynamicallyInvokable]
bool TryAdd(T item);
[__DynamicallyInvokable]
bool TryTake(out T item);
}除了CopyTo 之外的方法, 其余的都是该接口自己,基于堆栈的线程安全实现也就是加锁, 那为什么不调用堆栈数据结构中的SyncRoot 属性和Synchronized()方法来加锁实现同步?
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Task t = RunProgram();
            t.Wait();
        }

        static async Task RunProgram()
        {
            var taskStack = new ConcurrentStack<CustomTask>();
            var cts = new CancellationTokenSource();

            var taskSource = Task.Run(() => TaskProducer(taskStack));

            Task[] processors = new Task[4];
            for (int i = 1; i <= 4; i++)
            {
                string processorId = i.ToString();
                processors[i - 1] = Task.Run(
                    () => TaskProcessor(taskStack, "Processor " + processorId, cts.Token));
            }

            await taskSource;
            cts.CancelAfter(TimeSpan.FromSeconds(2));

            await Task.WhenAll(processors);
        }

        static async Task TaskProducer(ConcurrentStack<CustomTask> stack)
        {
            for (int i = 1; i <= 20; i++)
            {
                await Task.Delay(50);
                var workItem = new CustomTask { Id = i };
                stack.Push(workItem);
                Console.WriteLine("Task {0} has been posted", workItem.Id);
            }
        }

        static async Task TaskProcessor(
            ConcurrentStack<CustomTask> stack, string name, CancellationToken token)
        {
            await GetRandomDelay();
            do
            {
                CustomTask workItem;
                bool popSuccesful = stack.TryPop(out workItem);
                if (popSuccesful)
                {
                    Console.WriteLine("Task {0} has been processed by {1}", workItem.Id, name);
                }

                await GetRandomDelay();
            }
            while (!token.IsCancellationRequested);
        }

        static Task GetRandomDelay()
        {
            int delay = new Random(DateTime.Now.Millisecond).Next(1, 500);
            return Task.Delay(delay);
        }

        class CustomTask
        {
            public int Id { get; set; }
        }
    }
}

参照:
C# Synchronized 和 SyncRoot 完结线程同步的源码深入分析及泛型集结的线程安全访谈
SyncRoot 属性

Program

尽管调用得是会集类的SyncRoot属性的话,其锁是目的等级的,而static 则是项目级其余。具体的悔过再商讨下。

3、ConcurrentQueue类:安全队列

BlockingCollection类型这么些集结类依旧挺风趣的,他促成了IProducerConsumerCollection<T>的兼具办法,能够达成其余自定义类型的线程安全。越发是他的计时不通操作,具体代码示例请看:
何以:在 BlockingCollection 中各种增进和抽取项
BlockingCollection 概述

bob体育平台 7bob体育平台 8

using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Task t = RunProgram();
            t.Wait();
        }

        static async Task RunProgram()
        {
            var taskQueue = new ConcurrentQueue<CustomTask>();
            var cts = new CancellationTokenSource();

            var taskSource = Task.Run(() => TaskProducer(taskQueue));

            Task[] processors = new Task[4];
            for (int i = 1; i <= 4; i++)
            {
                string processorId = i.ToString();
                processors[i - 1] = Task.Run(
                    () => TaskProcessor(taskQueue, "Processor " + processorId, cts.Token));
            }

            await taskSource;
            cts.CancelAfter(TimeSpan.FromSeconds(2));

            await Task.WhenAll(processors);
        }

        static async Task TaskProducer(ConcurrentQueue<CustomTask> queue)
        {
            for (int i = 1; i <= 20; i++)
            {
                await Task.Delay(50);
                var workItem = new CustomTask { Id = i };
                queue.Enqueue(workItem);
                Console.WriteLine("插入Task {0} has been posted ThreadID={1}", workItem.Id, Thread.CurrentThread.ManagedThreadId);
            }
        }

        static async Task TaskProcessor(
            ConcurrentQueue<CustomTask> queue, string name, CancellationToken token)
        {
            CustomTask workItem;
            bool dequeueSuccesful = false;

            await GetRandomDelay();
            do
            {
                dequeueSuccesful = queue.TryDequeue(out workItem);
                if (dequeueSuccesful)
                {
                    Console.WriteLine("读取Task {0} has been processed by {1} ThreadID={2}",
                                        workItem.Id, name, Thread.CurrentThread.ManagedThreadId);
                }

                await GetRandomDelay();
            }
            while (!token.IsCancellationRequested);
        }

        static Task GetRandomDelay()
        {
            int delay = new Random(DateTime.Now.Millisecond).Next(1, 500);
            return Task.Delay(delay);
        }

        class CustomTask
        {
            public int Id { get; set; }
        }
    }
}

Program

4、ConcurrentDictionary类
  ConcurrentDictionary类写操作比使用锁的何奇之有字典(Dictionary卡塔尔国要慢的多,而读操作则要快些。由此对字典要多量的线程安全的读操作,ConcurrentDictionary类是最棒的选项
  ConcurrentDictionary类的达成利用了细粒度锁(fine-grained locking卡塔尔技巧,那在四线程写入方面比选拔锁的司空眼惯的字典(也被誉为粗粒度锁卡塔尔(英语:State of Qatar)

bob体育平台 9bob体育平台 10

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            var concurrentDictionary = new ConcurrentDictionary<int, string>();
            var dictionary = new Dictionary<int, string>();

            var sw = new Stopwatch();

            sw.Start();
            for (int i = 0; i < 1000000; i++)
            {
                lock (dictionary)
                {
                    dictionary[i] = Item;
                }
            }
            sw.Stop();
            Console.WriteLine("Writing to dictionary with a lock: {0}", sw.Elapsed);

            sw.Restart();
            for (int i = 0; i < 1000000; i++)
            {
                concurrentDictionary[i] = Item;
            }
            sw.Stop();
            Console.WriteLine("Writing to a concurrent dictionary: {0}", sw.Elapsed);

            sw.Restart();
            for (int i = 0; i < 1000000; i++)
            {
                lock (dictionary)
                {
                    CurrentItem = dictionary[i];
                }
            }
            sw.Stop();
            Console.WriteLine("Reading from dictionary with a lock: {0}", sw.Elapsed);

            sw.Restart();
            for (int i = 0; i < 1000000; i++)
            {
                CurrentItem = concurrentDictionary[i];
            }
            sw.Stop();
            Console.WriteLine("Reading from a concurrent dictionary: {0}", sw.Elapsed);
        }

        const string Item = "Dictionary item";
        public static string CurrentItem;
    }
}

Program

版权声明:本文由bob体育app发布于编程,转载请注明出处:C# 中的线程安全集合类