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

同步

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


使用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();
        }
    }
}


所有的实例代码



你可能感兴趣的:(23 C# 第十九章(一) 基于 .Net 4 TPL 的同步和线程处理模式)