同步
多线程编程的复杂性在于识别多个线程可能同时访问的数据。同步可以防止数据的同时访问。
使用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
// 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();
}
}
}
所有的实例代码