23 C# 第十九章(一) 基于 .Net 4 TPL 的同步和线程处理模式

来源:互联网 发布:ios存照片软件 编辑:程序博客网 时间:2024/06/07 12:05

同步

多线程编程的复杂性在于识别多个线程可能同时访问的数据。同步可以防止数据的同时访问。


使用Monitor 

Monitor 可以看作是一个监视器,来阻止第二个线程进入一个受保护的代码段,直到第一个线程退出代码段。

Monitor主要使用的是Monitor.Enter() 和 Monitor.Exit(),但要保证Monitor.Exit()一定被调用,防止其长时间的阻止其他线程进入。


Monitor 代码实例:


using System;using System.Collections.Generic;using System.Text;using System.Threading;using System.Threading.Tasks;namespace TPL_Sync_Sample_Simple_Monitor{    class Program    {        readonly static object _Sync = new object();        const int _Total = 1000;        static long _Count = 0;        static void Main(string[] args)        {            Task task = Task.Factory.StartNew(Decrement);            for (int i = 0; i < _Total; i++)            {                bool lockToken = false;                Monitor.Enter(_Sync, ref lockToken);                try                {                    _Count++;                    Console.WriteLine("Count ++ : {0}", _Count);                }                finally                {                    if (lockToken)                    {                        Monitor.Exit(_Sync);                    }                }            }            task.Wait();            Console.WriteLine("Count = {0}", _Count);            Console.ReadKey();        }        static void Decrement()        {            for (int i = 0; i < _Total; i++)            {                bool lockToken = false;                Monitor.Enter(_Sync, ref lockToken);                try                {                    _Count--;                    Console.WriteLine("Count -- : {0}", _Count);                }                finally                {                    if (lockToken)                    {                        Monitor.Exit(_Sync);                    }                }            }        }    }}




使用Lock

当使用Monitor时会频繁的使用try / finally 来保证一定会调用Monitor.Exit()。这里提供了一种 lock 的锁定方式。效果与Monitor一样。

Lock 代码实例:


using System;using System.Collections.Generic;using System.Text;using System.Threading;using System.Threading.Tasks;namespace TPL_Sync_Sample_Simple_Lock{    class Program    {        readonly static object _Sync = new object();        const int _Total = 1000;        static long _Count = 0;        static void Main(string[] args)        {            Task task = Task.Factory.StartNew(Decrement);            for (int i = 0; i < _Total; i++)            {                lock (_Sync)                {                    _Count++;                    Console.WriteLine("++ {0}  ", _Count);                }            }            task.Wait();            Console.WriteLine("Count = {0}", _Count);            Console.ReadKey();        }        static void Decrement()        {            for (int i = 0; i < _Total; i++)            {                lock (_Sync)                {                    _Count--;                    Console.WriteLine("-- {0}  ", _Count);                }            }        }    }}




加锁对象的选择

无论使用lock 还是 Monitor,都要小心的选择所要lock的对象。

例如程序中定义的对象

readonly static object _Sync = new object();

readonly 保证在 Monitor.Enter和Monitor.Exit中间值不会被改变。

private 保证类外的同步块不会访问它。

而且同步对象不能是值类型的。


volatile

编译器和CPU会对代码进行优化,使指令不按其编码顺序执行,或取消某些指令,这在多线程程序中会造成出乎意料的结果。volatile 关键字强迫所有的读写操作都在代码指定的位置执行。



更多同步类型:


System.Threading.Mutex


System.Threading.Mutex  代码实例:

// This example shows how a Mutex is used to synchronize access// to a protected resource. Unlike Monitor, Mutex can be used with// WaitHandle.WaitAll and WaitAny, and can be passed across// AppDomain boundaries.using System;using System.Threading;class Test{    // Create a new Mutex. The creating thread does not own the    // Mutex.    private static Mutex mut = new Mutex();    private const int numIterations = 1;    private const int numThreads = 3;    static void Main()    {        // Create the threads that will use the protected resource.        for (int i = 0; i < numThreads; i++)        {            Thread myThread = new Thread(new ThreadStart(MyThreadProc));            myThread.Name = String.Format("Thread{0}", i + 1);            myThread.Start();        }        // The main thread exits, but the application continues to        // run until all foreground threads have exited.        Console.ReadKey();    }    private static void MyThreadProc()    {        for (int i = 0; i < numIterations; i++)        {            UseResource();        }    }    // This method represents a resource that must be synchronized    // so that only one thread at a time can enter.    private static void UseResource()    {        // Wait until it is safe to enter.        mut.WaitOne();        Console.WriteLine("{0} has entered the protected area",            Thread.CurrentThread.Name);        // Place code to access non-reentrant resources here.        // Simulate some work.        Thread.Sleep(500);        Console.WriteLine("{0} is leaving the protected area\r\n",            Thread.CurrentThread.Name);        // Release the Mutex.        mut.ReleaseMutex();    }}



WaitHandle


WaitHandle代码实例:

using System;using System.Threading;using System.Threading.Tasks;namespace TPL_Sync_Sample_WaitHandle_Task{    class Program    {        static ManualResetEventSlim MainSignaledResetEvent = new ManualResetEventSlim();        static ManualResetEventSlim DoWorkSignaledResetEvent = new ManualResetEventSlim();        public static void DoWork()        {            Console.WriteLine("DoWork Start().");            DoWorkSignaledResetEvent.Set();            MainSignaledResetEvent.Wait();            Console.WriteLine("DoWork End().");        }        static void Main(string[] args)        {            {                Console.WriteLine("Main Start().");                Task task = Task.Factory.StartNew(DoWork);                DoWorkSignaledResetEvent.Wait();                Console.WriteLine("Main Execute.");                MainSignaledResetEvent.Set();                task.Wait();                Console.WriteLine("Main End().");                Console.ReadKey();            }        }    }}





semaphore

http://msdn.microsoft.com/zh-cn/library/vstudio/system.threading.semaphore.aspx


semaphore代码实例:

using System;using System.Threading;public class Example{    // A semaphore that simulates a limited resource pool.    //    private static Semaphore _pool;    // A padding interval to make the output more orderly.    private static int _padding;    public static void Main()    {        // Create a semaphore that can satisfy up to three        // concurrent requests. Use an initial count of zero,        // so that the entire semaphore count is initially        // owned by the main program thread.        //        _pool = new Semaphore(0, 3);        // Create and start five numbered threads.         //        for (int i = 1; i <= 5; i++)        {            Thread t = new Thread(new ParameterizedThreadStart(Worker));            // Start the thread, passing the number.            //            t.Start(i);        }        // Wait for half a second, to allow all the        // threads to start and to block on the semaphore.        //        Thread.Sleep(500);        // The main thread starts out holding the entire        // semaphore count. Calling Release(3) brings the         // semaphore count back to its maximum value, and        // allows the waiting threads to enter the semaphore,        // up to three at a time.        //        Console.WriteLine("Main thread calls Release(3).");        _pool.Release(3);        Console.WriteLine("Main thread exits.");        Console.ReadKey();    }    private static void Worker(object num)    {        // Each worker thread begins by requesting the        // semaphore.        Console.WriteLine("Thread {0} begins " +            "and waits for the semaphore.", num);        _pool.WaitOne();        // A padding interval to make the output more orderly.        int padding = Interlocked.Add(ref _padding, 100);        Console.WriteLine("Thread {0} enters the semaphore.", num);        // The thread's "work" consists of sleeping for         // about a second. Each thread "works" a little         // longer, just to make the output more orderly.        //        Thread.Sleep(1000 + padding);        Console.WriteLine("Thread {0} releases the semaphore.", num);        Console.WriteLine("Thread {0} previous semaphore count: {1}",            num, _pool.Release());    }}



本地线程存储

利用线程本地存储,线程可以有一个专属的变量实例,实现了一种隔离的方式。

线程本地存储代码实例:

using System;using System.Threading;namespace TPL_Sync_Sample_ThreadLocal{    class Program    {        static ThreadLocal<int> _Count = new ThreadLocal<int>(()=>0);        public static int Count        {            get { return _Count.Value; }            set { _Count.Value = value; }        }        static void Main(string[] args)        {            Thread thread = new Thread(Decrement);            thread.Start();            for (int i = 0; i < 1000; i++)            {                Count++;            }            thread.Join();            Console.WriteLine("Main Count = {0} ", Count);            Console.ReadKey();        }        static void Decrement()        {            for (int i = 0; i < 1000; i++)            {                Count--;            }            Console.WriteLine("Thread Count = {0}", Count);        }    }}




Timer 的使用

1)  System.Windows.Forms.Timer 
    为用户界面编程


2)  System.Timers.Timer
    如果需要容纳在一个IContainer中,则使用它


3)  System.Threading.Timer
    如果不需要System.Timers.Timer的专门功能,则可以使用它,它是一个稍微轻量级的实现。


System.Threading.Timer 代码实例:

http://msdn.microsoft.com/zh-cn/library/vstudio/system.threading.timer.aspx

using System;using System.Threading;class TimerExample{    static void Main()    {        // Create an event to signal the timeout count threshold in the        // timer callback.        AutoResetEvent autoEvent = new AutoResetEvent(false);        StatusChecker statusChecker = new StatusChecker(10);        // Create an inferred delegate that invokes methods for the timer.        TimerCallback tcb = statusChecker.CheckStatus;        // Create a timer that signals the delegate to invoke         // CheckStatus after one second, and every 1/4 second         // thereafter.        Console.WriteLine("{0} Creating timer.\n", DateTime.Now.ToString("h:mm:ss.fff"));        Timer stateTimer = new Timer(tcb, autoEvent, 1000, 250);        // When autoEvent signals, change the period to every        // 1/2 second.        autoEvent.WaitOne(5000, false);        stateTimer.Change(0, 500);        Console.WriteLine("\nChanging period.\n");        // When autoEvent signals the second time, dispose of         // the timer.        autoEvent.WaitOne(5000, false);        stateTimer.Dispose();        Console.WriteLine("\nDestroying timer.");        Console.ReadKey();    }}class StatusChecker{    private int invokeCount;    private int maxCount;    public StatusChecker(int count)    {        invokeCount = 0;        maxCount = count;    }    // This method is called by the timer delegate.    public void CheckStatus(Object stateInfo)    {        AutoResetEvent autoEvent = (AutoResetEvent)stateInfo;        Console.WriteLine("{0} Checking status {1,2}.", DateTime.Now.ToString("h:mm:ss.fff"), (++invokeCount).ToString());        if (invokeCount == maxCount)        {            // Reset the counter and signal Main.            invokeCount = 0;            autoEvent.Set();        }    }}


所有的实例代码



原创粉丝点击