• C#线程安全类型


    1、IProducerConsumerCollection (线程安全接口)
      此接口的所有实现必须都启用此接口的所有成员,若要从多个线程同时使用。

    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);
            }
        }
    }
    SafeStack
    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!!");
            }
        }
    }
    Program

    2、ConcurrentStack类:安全堆栈

    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; }
            }
        }
    }
    Program

    3、ConcurrentQueue类:安全队列

    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)技术,这在多线程写入方面比使用锁的通常的字典(也被称为粗粒度锁)

    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

    5、ConcurrentBag类

    namespace ConsoleApp1
    {
        class CrawlingTask
        {
            public string UrlToCrawl { get; set; }
    
            public string ProducerName { get; set; }
        }
    }
    CrawlingTask
    using System.Collections.Generic;
    
    namespace ConsoleApp1
    {
        static class Module
        {
            public static Dictionary<string, string[]> _contentEmulation = new Dictionary<string, string[]>();
    
            public static void CreateLinks()
            {
                _contentEmulation["http://microsoft.com/"] = new[] { "http://microsoft.com/a.html", "http://microsoft.com/b.html" };
                _contentEmulation["http://microsoft.com/a.html"] = new[] { "http://microsoft.com/c.html", "http://microsoft.com/d.html" };
                _contentEmulation["http://microsoft.com/b.html"] = new[] { "http://microsoft.com/e.html" };
    
                _contentEmulation["http://google.com/"] = new[] { "http://google.com/a.html", "http://google.com/b.html" };
                _contentEmulation["http://google.com/a.html"] = new[] { "http://google.com/c.html", "http://google.com/d.html" };
                _contentEmulation["http://google.com/b.html"] = new[] { "http://google.com/e.html", "http://google.com/f.html" };
                _contentEmulation["http://google.com/c.html"] = new[] { "http://google.com/h.html", "http://google.com/i.html" };
    
                _contentEmulation["http://facebook.com/"] = new[] { "http://facebook.com/a.html", "http://facebook.com/b.html" };
                _contentEmulation["http://facebook.com/a.html"] = new[] { "http://facebook.com/c.html", "http://facebook.com/d.html" };
                _contentEmulation["http://facebook.com/b.html"] = new[] { "http://facebook.com/e.html" };
    
                _contentEmulation["http://twitter.com/"] = new[] { "http://twitter.com/a.html", "http://twitter.com/b.html" };
                _contentEmulation["http://twitter.com/a.html"] = new[] { "http://twitter.com/c.html", "http://twitter.com/d.html" };
                _contentEmulation["http://twitter.com/b.html"] = new[] { "http://twitter.com/e.html" };
                _contentEmulation["http://twitter.com/c.html"] = new[] { "http://twitter.com/f.html", "http://twitter.com/g.html" };
                _contentEmulation["http://twitter.com/d.html"] = new[] { "http://twitter.com/h.html" };
                _contentEmulation["http://twitter.com/e.html"] = new[] { "http://twitter.com/i.html" };
            }
        }
    }
    Module
    using System;
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Program
        {
            static void Main(string[] args)
            {
                Module.CreateLinks();
                Task t = RunProgram();
                t.Wait();
            }
    
            static async Task RunProgram()
            {
                var bag = new ConcurrentBag<CrawlingTask>();
    
                string[] urls = new[] { "http://microsoft.com/", "http://google.com/", "http://facebook.com/", "http://twitter.com/" };
    
                var crawlers = new Task[4];
                for (int i = 1; i <= 4; i++)
                {
                    string crawlerName = "Crawler " + i.ToString();
                    bag.Add(new CrawlingTask { UrlToCrawl = urls[i - 1], ProducerName = "root" });
                    crawlers[i - 1] = Task.Run(() => Crawl(bag, crawlerName));
                }
    
                await Task.WhenAll(crawlers);
            }
    
            static async Task Crawl(ConcurrentBag<CrawlingTask> bag, string crawlerName)
            {
                CrawlingTask task;
                //尝试从bag中取出对象
                while (bag.TryTake(out task))
                {
                    IEnumerable<string> urls = await GetLinksFromContent(task);
                    if (urls != null)
                    {
                        foreach (var url in urls)
                        {
                            var t = new CrawlingTask
                            {
                                UrlToCrawl = url,
                                ProducerName = crawlerName
                            };
                            //将子集插入到bag中 
                            bag.Add(t);
                        }
                    }
                    Console.WriteLine("Indexing url {0} posted by {1} is completed by {2}!",
                        task.UrlToCrawl, task.ProducerName, crawlerName);
                }
            }
    
            static async Task<IEnumerable<string>> GetLinksFromContent(CrawlingTask task)
            {
                await GetRandomDelay();
    
                if (Module._contentEmulation.ContainsKey(task.UrlToCrawl)) return Module._contentEmulation[task.UrlToCrawl];
    
                return null;
            }
    
            static Task GetRandomDelay()
            {
                int delay = new Random(DateTime.Now.Millisecond).Next(150, 200);
                return Task.Delay(delay);
            }
    
    
        }
    }
    Program

    6、BlockingCollection类
      BlockingCollection类: 我们能够改变任务存储在阻塞集合中的方式。默认情况下它使用的是ConcurrentQueue容器,但是我们能够使用任何实现了IProducerConsumerCollection泛型接口的集合。

    namespace ConsoleApp1
    {
        class CustomTask
        {
            public int Id { get; set; }
        }
    }
    CustomTask
    using System;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        static class Module
        {
            public static Task GetRandomDelay()
            {
                int delay = new Random(DateTime.Now.Millisecond).Next(1, 500);
                return Task.Delay(delay);
            }
        }
    }
    Module
    using System;
    using System.Collections.Concurrent;
    using System.Threading.Tasks;
    
    namespace ConsoleApp1
    {
        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine("Using a Queue inside of BlockingCollection");
                Console.WriteLine();
                Task t = RunProgram();
                t.Wait();
    
                //Console.WriteLine();
                //Console.WriteLine("Using a Stack inside of BlockingCollection");
                //Console.WriteLine();
                //Task t = RunProgram(new ConcurrentStack<CustomTask>());
                //t.Wait();
            }
    
            static async Task RunProgram(IProducerConsumerCollection<CustomTask> collection = null)
            {
                var taskCollection = new BlockingCollection<CustomTask>();
                if (collection != null)
                    taskCollection = new BlockingCollection<CustomTask>(collection);
                //初始化collection中的数据
                var taskSource = Task.Run(() => TaskProducer(taskCollection));
    
                Task[] processors = new Task[4];
                for (int i = 1; i <= 4; i++)
                {
                    string processorId = "Processor " + i;
                    processors[i - 1] = Task.Run(
                        () => TaskProcessor(taskCollection, processorId));
                }
    
                await taskSource;
    
                await Task.WhenAll(processors);
            }
            /// <summary>
            /// 初始化collection中的数据
            /// </summary>
            /// <param name="collection"></param>
            /// <returns></returns>
            static async Task TaskProducer(BlockingCollection<CustomTask> collection)
            {
                for (int i = 1; i <= 20; i++)
                {
                    await Task.Delay(20);
                    var workItem = new CustomTask { Id = i };
                    collection.Add(workItem);
                    Console.WriteLine("Task {0} has been posted", workItem.Id);
                }
                collection.CompleteAdding();
            }
            /// <summary>
            /// 打印collection中的数据
            /// </summary>
            /// <param name="collection"></param>
            /// <param name="name"></param>
            /// <returns></returns>
            static async Task TaskProcessor(
                BlockingCollection<CustomTask> collection, string name)
            {
                await Module.GetRandomDelay();
                foreach (CustomTask item in collection.GetConsumingEnumerable())
                {
                    Console.WriteLine("Task {0} has been processed by {1}", item.Id, name);
                    await Module.GetRandomDelay();
                }
            }
        }
    }
    Program

    7、使用ThreadStatic特性
      ThreadStatic特性是最简单的TLS使用,且只支持静态字段,只需要在字段上标记这个特性就可以了

    using System;
    using System.Threading;
    
    namespace ConsoleApp1
    {
        class Program
        {
            //TLS中的str变量
            //可以看到,str静态字段在两个线程中都是独立存储的,互相不会被修改。
            [ThreadStatic]
            static string str = "hehe";
    
            static void Main(string[] args)
            {
                //另一个线程只会修改自己TLS中的hehe
                Thread th = new Thread(() => { str = "Mgen"; Display(); });
                th.Start();
                th.Join();
                Display();
            }
            static void Display()
            {
                Console.WriteLine("{0} {1}", Thread.CurrentThread.ManagedThreadId, str);
            }
    
        }
    }
    Program

    8、使用命名的LocalDataStoreSlot类型
      显然ThreadStatic特性只支持静态字段太受限制了。.NET线程类型中的LocalDataStoreSlot提供更好的TLS支持。我们先来看看命名的LocalDataStoreSlot类型,可以通过Thread.AllocateNamedDataSlot来分配一个命名的空间,通过Thread.FreeNamedDataSlot来销毁一个命名的空间。空间数据的获取和设置则通过Thread类型的GetData方法和SetData方法。

    using System;
    using System.Threading;
    
    namespace ConsoleApp1
    {
        class Program
        {
            static void Main(string[] args)
            {
                //创建Slot
                LocalDataStoreSlot slot = Thread.AllocateNamedDataSlot("slot");
    
                //设置TLS中的值
                Thread.SetData(slot, "hehe");
    
                //修改TLS的线程
                Thread th = new Thread(() =>
                {
                    Thread.SetData(slot, "Mgen");
                    Display();
                });
    
                th.Start();
                th.Join();
                Display();
    
                //清除Slot
                Thread.FreeNamedDataSlot("slot");
            }
    
            //显示TLS中Slot值
            static void Display()
            {
                LocalDataStoreSlot dataslot = Thread.GetNamedDataSlot("slot");
                Console.WriteLine("{0} {1}", Thread.CurrentThread.ManagedThreadId, Thread.GetData(dataslot));
            }
    
        }
    }
    Program

    9、使用未命名的LocalDataStoreSlot类型
      线程同样支持未命名的LocalDataStoreSlot,未命名的LocalDataStoreSlot不需要手动清除,分配则需要Thread.AllocateDataSlot方法。注意由于未命名的LocalDataStoreSlot没有名称,因此无法使用Thread.GetNamedDataSlot方法,只能在多个线程中引用同一个LocalDataStoreSlot才可以对TLS空间进行操作,将上面的命名的LocalDataStoreSlot代码改成未命名的LocalDataStoreSlot执行

    using System;
    using System.Threading;
    
    namespace ConsoleApp1
    {
        class Program
        {
            //静态LocalDataStoreSlot变量
            static LocalDataStoreSlot slot;
    
            static void Main(string[] args)
            {
                //创建Slot
                slot = Thread.AllocateDataSlot();
    
                //设置TLS中的值
                Thread.SetData(slot, "hehe");
    
                //修改TLS的线程
                Thread th = new Thread(() =>
                {
                    Thread.SetData(slot, "Mgen");
                    Display();
    
                });
    
                th.Start();
                th.Join();
                Display();
            }
    
            //显示TLS中Slot值
            static void Display()
            {
                Console.WriteLine("{0} {1}", Thread.CurrentThread.ManagedThreadId, Thread.GetData(slot));
            }
    
        }
    }
    Program

    10、使用.NET 4.0的ThreadLocal<T>类型
      .NET 4.0在线程方面加入了很多东西,其中就包括ThreadLocal<T>类型,他的出现更大的简化了TLS的操作。ThreadLocal<T>类型和Lazy<T>惊人相似,构造函数参数是Func<T>用来创建对象(当然也可以理解成对象的默认值),然后用Value属性来得到或者设置这个对象。
      ThreadLocal的操作或多或少有点像上面的未命名的LocalDataStoreSlot,但ThreadLocal感觉更简洁更好理解。

    using System;
    using System.Threading;
    
    namespace ConsoleApp1
    {
        class Program
        {
            static ThreadLocal<string> local;
    
            static void Main(string[] args)
            {
                //创建ThreadLocal并提供默认值
                local = new ThreadLocal<string>(() => "hehe");
    
                //修改TLS的线程
                Thread th = new Thread(() =>
                {
    
                    local.Value = "Mgen";
                    Display();
                });
    
                th.Start();
                th.Join();
                Display();
            }
    
            //显示TLS中数据值
            static void Display()
            {
                Console.WriteLine("{0} {1}", Thread.CurrentThread.ManagedThreadId, local.Value);
            }
    
        }
    }
    Program
  • 相关阅读:
    linux常用命令
    ANAFI EXTENOED无人机(1)环境配置和基础开发
    无人机自主降落
    ROS开发(1)安装环境
    bebop无人机(1)环境配置和基础开发
    YOLO标注软件
    Python2与Python3之间切换
    python实现IOU计算
    读取多个(海康大华)网络摄像头的视频流 (使用opencv-python),解决实时读取延迟问题
    如何到外面的世界看看
  • 原文地址:https://www.cnblogs.com/scmail81/p/9508785.html
Copyright © 2020-2023  润新知