一、多线程入的好处
在应用程序中使用多个线程的好处是每个线程都可以异步执行。对于Windows应用程序,耗时的任务可以在后台执行,而使应用程序的窗口和控件保持响应。对于服务器应用程序,多线程处理提供了用不同线程处理每个传入请求的能力。否则,在完全满足前一个请求之前,将无法处理每个新请求。
二、多线程带来的并发问题
然而,线程的异步特性意味着必须协调对资源(如文件句柄、网络连接和内存)的访问、即必须确保任何共享数据都处于被保护状态。否则,两个或更多的线程可能在同一时间访问相同的资源,而每个线程都不知道其他线程的操作。结果将产生不可预知的数据损坏。
案例测试代码:
-
namespace MultiThreadPrint
-
{
-
public class CPrinter
-
{
-
public void PrintNumbers()
-
{
-
Console.WriteLine("{0} is excuting PrintNumbers()", Thread.CurrentThread.Name);
-
for (int i = 0; i < 10; i++)
-
{
-
Random r = new Random();
-
Thread.Sleep(1000 * r.Next(5));
-
Console.Write("{0}, ", i);
-
}
-
Console.WriteLine();
-
}
-
};
-
class Program
-
{
-
static void Main(string[] args)
-
{
-
CPrinter p = new CPrinter();
-
// Thread threads[] = new Thread[10]; /* Note that this is the wrong way. */
-
Thread[] threads = new Thread[10]; // This is create array,not concrete thread.
-
for (int i = 0; i < 10; i++)
-
{
-
threads[i] = new Thread(new ThreadStart(p.PrintNumbers));
-
threads[i].Name = "Worker thread #" + i.ToString();
-
}
-
foreach (Thread t in threads)
-
{
-
t.Start();
-
}
-
Console.ReadLine();
-
}
-
}
-
}
图1
由图1可以看出,十个线程共享一个CPrinter对象时,输出的数据并不是“0,1,2,3,4,5,6,7,8,9”这样有规律的输出,而是出现了各个数据的穿插。说明有某一个时间片线程A调用了CPrinter的PrintNumbers(),别一个时间片线程B也调用了CPrinter的PrintNumbers(),很明显线程A的循环并没有完全执行。而CPrinter对象在各个不同线程的相互竞争中所以打印出了这图1的没规律数据。
三、实现数据同步的相关方法
对于整数数据类型简单操作,可以用Interlocked类的成员来实现线程同步。对于其他所有数据类型和非线程安全的资源,需要使用lock\mutex等机制来处理,详情后解。下面给出Interlocked的案例。
-
namespace InterlockedHandle
-
{
-
class Program
-
{
-
//0 for false, 1 for true.
-
private static int usingResource = 0;
-
private static Object currentMso;
-
private static Object globalMso = new Object();
-
private const int numThreadIterations = 5;
-
private const int numThreads = 10;
-
static void Main()
-
{
-
Thread myThread;
-
Random rnd = new Random();
-
for (int i = 0; i < numThreads; i++)
-
{
-
myThread = new Thread(new ThreadStart(MyThreadProc));
-
myThread.Name = String.Format("Thread{0}", i + 1);
-
//Wait a random amount of time before starting next thread.
-
Thread.Sleep(rnd.Next(0, 1000));
-
myThread.Start();
-
}
-
Console.ReadLine();
-
}
-
private static void MyThreadProc()
-
{
-
for (int i = 0; i < numThreadIterations; i++)
-
{
-
UseResource();
-
//Wait 1 second before next attempt.
-
Thread.Sleep(1000);
-
}
-
}
-
//A simple method that denies reentrancy.
-
static bool UseResource()
-
{
-
//0 indicates that the method is not in use.
-
if (0 == Interlocked.Exchange(ref usingResource, 1))
-
{
-
Console.WriteLine("{0} acquired the lock", Thread.CurrentThread.Name);
-
//Code to access a resource that is not thread safe would go here.
-
//Simulate some work
-
Thread.Sleep(500);
-
Console.WriteLine("{0} exiting lock", Thread.CurrentThread.Name);
-
//Release the lock
-
Interlocked.Exchange(ref usingResource, 0);
-
return true;
-
}
-
else
-
{
-
Console.WriteLine(" {0} was denied the lock", Thread.CurrentThread.Name);
-
return false;
-
}
-
}
-
}
-
}
图2
图2可以看到usingResource 作为一个整型数据类型,当多个线程想对异步其进行访问时,因为这存在数据的同步问题。这里使用了Interlocked来使用每个线程要想访问usingResource,必须等待别一个线程使用完退出后才行!这样保证了数据的安全。
参考文献
MSDN--Interlocked 类
《C#与.NET4高级程序设计(第5版)》
阅读(2806) | 评论(0) | 转发(0) |