如果有兴趣的请仔细阅读下面的代码。注意其中代码段的顺序,思考一下,这些代码的顺序能否互相调换,为什么?这应该对学习很有帮助的。
using System.Net;using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Diagnostics;
namespace Webb.Study
{
class TestThread
{
static Mutex m_Mutex = new Mutex();
static Thread[] m_testThreads = new Thread[10];
static int m_threadIndex = 0;
static void ThreadCallBack()
{
TestThread.m_Mutex.WaitOne();
int m_index = m_threadIndex;
TestThread.m_Mutex.ReleaseMutex();
Console.WriteLine("Thread {0} start.",m_index);
for(int i=0;i<=10;i++)
{
TestThread.m_Mutex.WaitOne();
Console.WriteLine("Thread {0}: is running. {1}",m_index,i);
TestThread.m_Mutex.ReleaseMutex();
Thread.Sleep(100);
}
Console.WriteLine("Thread {0} end.",m_index);
}
public static void Main(String[] args)
{
Console.WriteLine("Main thread start.");
for(int i=0;i<TestThread.m_testThreads.Length;i++)
{
TestThread.m_threadIndex = i;
TestThread.m_testThreads[i] = new Thread(new ThreadStart(ThreadCallBack));
TestThread.m_testThreads[i].Start();
Thread.Sleep(100);
}
for(int i=0;i<TestThread.m_testThreads.Length;i++)
{
TestThread.m_testThreads[i].Join();
}
Console.WriteLine("Main thread exit.");
}
}
}
1、主函数中这两句能否互换?为什么?
TestThread.m_testThreads[i].Start();
Thread.Sleep(100);
这里存在一个BUG,与问题4相关,后面做解释。
2、CallBack函数中这两句能否互换?为什么?会有什么不同的结果?
TestThread.m_Mutex.ReleaseMutex();
Thread.Sleep(100);
这两句换不换没什么太大的关系,因为线程互斥就是对时间及资源的互斥,因此这里是先等0.1秒再释放互斥量还是先释放互斥量再等0.1秒没有本质的关系,只是在输出上有些停顿。
3、主函数能否写成这样?为什么?会有什么不同的结果?
public static void Main(String[] args)
{
Console.WriteLine("Main thread start.");
for(int i=0;i<TestThread.m_testThreads.Length;i++)
{
TestThread.m_threadIndex = i;
TestThread.m_testThreads[i] = new Thread(new ThreadStart(ThreadCallBack));
TestThread.m_testThreads[i].Start();
TestThread.m_testThreads[i].Join();
Thread.Sleep(100);
}
Console.WriteLine("Main thread exit.");
}
如果这两句相换的话,主线程(Main函数所在的线程)就会在每次起动子线程后等待它所起动的线程执行完毕后再开始运行,这样就会使子线程一个一个顺序起动,而且直到前一个完成后,后一个才开始启动,所以最后的结果就是子线程一个接一个的顺序执行,而主线程则像调用子函数一样,一个一个的等待。
4、这几句的作用是什么?那么程序中还存在什么样的问题?应该做怎样的修改?
TestThread.m_Mutex.WaitOne();
int m_index = m_threadIndex;
TestThread.m_Mutex.ReleaseMutex();
其实如果先想一下,这个例子中互斥的对象是什么,或者互斥的资源是什么,那么这个问题就很明白了。
其实这里互斥的是控制台的输出权,以及主函数与子线程函数的全局变量m_threadIndex共享问题。
因此,第四个问题其实是主函数与子线程函数关于全局变量m_threadIndex的共享问题,然而这里的程序设计上存在的BUG就是它只考虑了子函数与子函数的共享问题,而没有考虑主函数的问题,所以,问题1中,互换语句,将会做使共享资源无效,从而使变量的值出现混乱情况。改进的方法应该是在主线程与子线程中添加一个信号量。
修改后的代码参见下面的。
using System.Net;using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Diagnostics;
namespace Webb.Study
{
class TestThread
{
static Mutex m_Mutex = new Mutex();
static Thread[] m_testThreads = new Thread[10];
static int m_threadIndex = 0;
static AutoResetEvent[] m_events = new AutoResetEvent[] {new AutoResetEvent(false)};
static void ThreadCallBack()
{
// TestThread.m_Mutex.WaitOne();
int m_index = m_threadIndex;
m_events[0].Set();
// TestThread.m_Mutex.ReleaseMutex();
Console.WriteLine("Thread {0} start.",m_index);
for(int i=0;i<=10;i++)
{
TestThread.m_Mutex.WaitOne();
Console.WriteLine("Thread {0}: is running. {1}",m_index,i);
TestThread.m_Mutex.ReleaseMutex();
Thread.Sleep(100);
}
Console.WriteLine("Thread {0} end.",m_index);
}
public static void Main(String[] args)
{
Console.WriteLine("Main thread start.");
for(int i=0;i<TestThread.m_testThreads.Length;)
{
TestThread.m_testThreads[i] = new Thread(new ThreadStart(ThreadCallBack));
TestThread.m_testThreads[i].Start();
WaitHandle.WaitAll(TestThread.m_events);
TestThread.m_threadIndex = ++i;
m_events[0].Reset();
}
for(int i=0;i<TestThread.m_testThreads.Length;i++)
{
TestThread.m_testThreads[i].Join();
}
Console.WriteLine("Main thread exit.");
}
}
}