有趣的多线程编程
一个简单的例子
//UsingDelegate.cs
------------------------------------
using System;
using System.Threading;
public class Test
{
static void Main()
{
Counter foo = new Counter();
ThreadStart job = new ThreadStart(foo.Count);
Thread thread = new Thread(job);
thread.Start();
for (int i=0; i < 5; i++)
{
Console.WriteLine ("Main thread: {0}", i);
Thread.Sleep(1000);
}
}
}
public class Counter
{
public void Count()
{
for (int i=0; i < 10; i++) { Console.WriteLine ("Other thread: {0}", i);
Thread.Sleep(500);
}
}
}
线程中的参数传递
使用类、类的方法或类的属性都可以向线程传递参数:
public class UrlDownloader
{
string url;
public UrlDownloader (string url)
{
this.url = url;
}
public void Download()
{
WebClient wc = new WebClient();
Console.WriteLine("Downloading " + url); byte[] buffer = wc.DownloadData (url); string download = Encoding.ASCII.GetString(buffer); Console.WriteLine(download); Console.WriteLine("Download successful.");
//这里你可以将download进行保存等处理......
}
}
[... 在另一个类中使用它们...]
UrlDownloader downloader = new UrlDownloader (yourUrl);
new Thread (new ThreadStart (downloader.Download)).Start();
注意参数是如何传递的。
在.NET 2.0中还可以这样:
(CODE-BESIDE)
方式一:
ThreadStart starter = delegate { Download(yourUrl);
});
new Thread(starter).Start();
//使用线程池
WaitCallback callback = delegate (object state) { Download ((string)state); };ThreadPool.QueueUserWorkItem (callback, yourUrl);方式二(使用ParameterizedThreadStart):Thread t = new Thread (new ParameterizedThreadStart(DownloadUrl));t.Start (myUrl);static void DownloadUrl(object url){ // ....
}线程内部是如何进行的?看一下以下两个例子的运行结果:
//TestThread.cs
using System;using System.Threading;public class Test{ static int count=0; static void Main() { ThreadStart job = new ThreadStart(ThreadJob); Thread thread = new Thread(job); thread.Start(); for (int i=0; i < 5; i++) { count++; } thread.Join(); Console.WriteLine ("Final count: {0}", count); } static void ThreadJob() { for (int i=0; i < 5; i++) { count++; } }}//InnerDataThread.csusing System;using System.Threading;public class Test{ static int count=0; static void Main() { ThreadStart job = new ThreadStart(ThreadJob); Thread thread = new Thread(job); thread.Start(); for (int i=0; i < 5; i++) { int tmp = count; Console.WriteLine ("Read count={0}", tmp); Thread.Sleep(50); tmp++; Console.WriteLine ("Incremented tmp to {0}", tmp); Thread.Sleep(20); count = tmp; Console.WriteLine ("Written count={0}", tmp); Thread.Sleep(30); } thread.Join(); Console.WriteLine ("Final count: {0}", count); } static void ThreadJob() { for (int i=0; i < 5; i++) { int tmp = count; Console.WriteLine ("\t\t\t\tRead count={0}", tmp); Thread.Sleep(20); tmp++; Console.WriteLine ("\t\t\t\tIncremented tmp to {0}", tmp); Thread.Sleep(10); count = tmp; Console.WriteLine ("\t\t\t\tWritten count={0}", tmp); Thread.Sleep(40); } }}
Read count=0 Read count=0 Incremented tmp to 1 Written count=1Incremented tmp to 1Written count=1 Read count=1 Incremented tmp to 2Read count=1 Written count=2 Read count=2Incremented tmp to 2 Incremented tmp to 3Written count=2 Written count=3Read count=3 Read count=3Incremented tmp to 4 Incremented tmp to 4 Written count=4Written count=4 Read count=4Read count=4 Incremented tmp to 5 Written count=5Incremented tmp to 5Written count=5Read count=5Incremented tmp to 6Written count=6Final count: 6再比较下面这个例子:
//使用Monitor.Enter/Exit
//MonitorThread.cs
using System;
using System.Threading;
public class Test
{
static int count=0;
static readonly object countLock = new object();
static void Main()
{
ThreadStart job = new ThreadStart(ThreadJob);
Thread thread = new Thread(job);
thread.Start();
for (int i=0; i < 5; i++)
{
Monitor.Enter(countLock);
int tmp = count;
Console.WriteLine ("Read count={0}", tmp);
Thread.Sleep(50);
tmp++;
Console.WriteLine ("Incremented tmp to {0}", tmp);
Thread.Sleep(20);
count = tmp;
Console.WriteLine ("Written count={0}", tmp);
Monitor.Exit(countLock);
Thread.Sleep(30);
}
thread.Join();
Console.WriteLine ("Final count: {0}", count);
}
static void ThreadJob()
{
for (int i=0; i < 5; i++)
{
Monitor.Enter(countLock);
int tmp = count;
Console.WriteLine ("\t\t\t\tRead count={0}", tmp);
Thread.Sleep(20);
tmp++;
Console.WriteLine ("\t\t\t\tIncremented tmp to {0}", tmp);
Thread.Sleep(10);
count = tmp;
Console.WriteLine ("\t\t\t\tWritten count={0}", tmp);
Monitor.Exit(countLock);
Thread.Sleep(40);
}
}
}
结果与上例InnerDataThread.cs是不一样的,原因就在于Monitor的使用了。
Read count=0Incremented tmp to 1Written count=1 Read count=1 Incremented tmp to 2 Written count=2Read count=2Incremented tmp to 3Written count=3 Read count=3 Incremented tmp to 4 Written count=4Read count=4Incremented tmp to 5Written count=5 Read count=5 Incremented tmp to 6 Written count=6Read count=6Incremented tmp to 7Written count=7 Read count=7 Incremented tmp to 8 Written count=8Read count=8Incremented tmp to 9Written count=9 Read count=9 Incremented tmp to 10 Written count=10Final count: 10 |
下面使用lock来锁定线程:
// LockThread.cs
using System;
using System.Threading;
public class Test
{
static int count=0;
static readonly object countLock = new object();
static void Main()
{
ThreadStart job = new ThreadStart(ThreadJob);
Thread thread = new Thread(job);
thread.Start();
for (int i=0; i < 5; i++)
{
lock (countLock)
{
int tmp = count;
Console.WriteLine ("Read count={0}", tmp);
Thread.Sleep(50);
tmp++;
Console.WriteLine ("Incremented tmp to {0}", tmp);
Thread.Sleep(20);
count = tmp;
Console.WriteLine ("Written count={0}", tmp);
}
Thread.Sleep(30);
}
thread.Join();
Console.WriteLine ("Final count: {0}", count);
}
static void ThreadJob()
{
for (int i=0; i < 5; i++)
{
lock (countLock)
{
int tmp = count;
Console.WriteLine ("\t\t\t\tRead count={0}", tmp);
Thread.Sleep(20);
tmp++;
Console.WriteLine ("\t\t\t\tIncremented tmp to {0}", tmp);
if (count < 100)
throw new Exception();
Thread.Sleep(10);
count = tmp;
Console.WriteLine ("\t\t\t\tWritten count={0}", tmp);
}
Thread.Sleep(40);
}
}
}
结果如何?与MonitorThread.cs比较一下,再想想看。
死锁
// DeadLockSample.cs
// 分析一下为什么会发生死锁?
using System;using System.Threading;public class Test{ static readonly object firstLock = new object(); static readonly object secondLock = new object(); static void Main() { new Thread(new ThreadStart(ThreadJob)).Start(); // Wait until we're fairly sure the other thread // has grabbed firstLock Thread.Sleep(500); Console.WriteLine ("Locking secondLock"); lock (secondLock) { Console.WriteLine ("Locked secondLock"); Console.WriteLine ("Locking firstLock"); lock (firstLock) { Console.WriteLine ("Locked firstLock"); } Console.WriteLine ("Released firstLock"); } Console.WriteLine("Released secondLock"); } static void ThreadJob() { Console.WriteLine ("\t\t\t\tLocking firstLock"); lock (firstLock) { Console.WriteLine("\t\t\t\tLocked firstLock"); // Wait until we're fairly sure the first thread // has grabbed secondLock Thread.Sleep(1000); Console.WriteLine("\t\t\t\tLocking secondLock"); lock (secondLock) { Console.WriteLine("\t\t\t\tLocked secondLock"); } Console.WriteLine ("\t\t\t\tReleased secondLock"); } Console.WriteLine("\t\t\t\tReleased firstLock"); }}
Locking firstLock Locked firstLock Locking secondLock Locked secondLock Locking firstLock Locking secondLock |
因应之道,使用Queue和Monitor:
//QueueMonitorThread.cs
using System;using System.Collections;using System.Threading;public class Test{ static ProducerConsumer queue; static void Main() { queue = new ProducerConsumer(); new Thread(new ThreadStart(ConsumerJob)).Start(); Random rng = new Random(0); for (int i=0; i < 10; i++) { Console.WriteLine ("Producing {0}", i); queue.Produce(i); Thread.Sleep(rng.Next(1000)); } } static void ConsumerJob() { // Make sure we get a different random seed from the // first thread Random rng = new Random(1); // We happen to know we've only got 10 // items to receive for (int i=0; i < 10; i++) { object o = queue.Consume(); Console.WriteLine ("\t\t\t\tConsuming {0}", o); Thread.Sleep(rng.Next(1000)); } }}public class ProducerConsumer{ readonly object listLock = new object(); Queue queue = new Queue(); public void Produce(object o) { lock (listLock) { queue.Enqueue(o); if (queue.Count==1) { Monitor.Pulse(listLock); } } } public object Consume() { lock (listLock) { while (queue.Count==0) { Monitor.Wait(listLock); } return queue.Dequeue(); } }}
Producing 0 Consuming 0 Producing 1 Consuming 1 Producing 2 Consuming 2 Producing 3 Consuming 3 Producing 4 Producing 5 Consuming 4 Producing 6 Consuming 5 Consuming 6 Producing 7 Consuming 7 Producing 8 Consuming 8 Producing 9 Consuming 9 |