Wu.Country@侠缘

勤学似春起之苗,不见其增,日有所长; 辍学如磨刀之石,不见其损,日所有亏!

一段比较经典的多线程学习代码

一段比较经典的多线程学习代码。

1、用到了多线程的同步问题。
2、用到了多线程的顺序问题。

如果有兴趣的请仔细阅读下面的代码。注意其中代码段的顺序,思考一下,这些代码的顺序能否互相调换,为什么?这应该对学习很有帮助的。为了演示,让所有的线程都Sleep了一段时间。

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);

2、CallBack函数中这两句能否互换?为什么?会有什么不同的结果?
                TestThread.m_Mutex.ReleaseMutex();
                Thread.Sleep(100);

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.");
        }

4、这几句的作用是什么?那么程序中还存在什么样的问题?应该做怎样的修改?
   TestThread.m_Mutex.WaitOne();
   int m_index = m_threadIndex;
   TestThread.m_Mutex.ReleaseMutex();

仅做学习讨论。

posted on 2006-06-19 16:44 Wu.Country@侠缘 阅读(2505) 评论(5)  编辑 收藏 所属分类: 90.Blue journal

评论

#1楼  2006-06-21 09:16 路人 [未注册用户]

我答答看 ^_^
1. 换了后 就只有9个线程在跑。
2. 换了后 2、3、4、5号线程将不会跑
3. 若这样写,则10个线程将按序号顺序跑
4. 防止多个线程中的对m_index赋值操作同时进行。
感觉这里用互斥有点多余,m_index是局部变量,不用互斥操作。   回复  引用    

#2楼 [楼主] 2006-06-30 16:03 Wu.Country@侠缘      

写个说明吧,最近忙了些,忘记写说明了。今天补上。
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、主函数能否写成这样?为什么?会有什么不同的结果?
如果这两句相换的话,主线程(Main函数所在的线程)就会在每次起动子线程后等待它所起动的线程执行完毕后再开始运行,这样就会使子线程一个一个顺序起动,而且直到前一个完成后,后一个才开始启动,所以最后的结果就是子线程一个接一个的顺序执行,而主线程则像调用子函数一样,一个一个的等待。

4、这几句的作用是什么?那么程序中还存在什么样的问题?应该做怎样的修改?
其实如果先想一下,这个例子中互斥的对象是什么,或者互斥的资源是什么,那么这个问题就很明白了。
其实这里互斥的是控制台的输出权,以及主函数与子线程函数的全局变量m_threadIndex共享问题。
因此,第四个问题其实是主函数与子线程函数关于全局变量m_threadIndex的共享问题,然而这里的程序设计上存在的BUG就是它只考虑了子函数与子函数的共享问题,而没有考虑主函数的问题,所以,问题1中,互换语句,将会做使共享资源无效,从而使变量的值出现混乱情况。改进的方法应该是在主线程与子线程中添加一个信号量。
修改后的代码参见下面的。   回复  引用  查看    

#3楼 [楼主] 2006-06-30 16:06 Wu.Country@侠缘      

修改后的代码。
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.");
        }

    }

}
  回复  引用  查看    

#4楼 [楼主] 2006-06-30 16:12 Wu.Country@侠缘      

对第二个问题再添加一点小的说明,互换后,因为前后每个线程相差有0.1秒,而每个线程及主线程都只是小的测试,这0.1秒对于这个小例子来说就像是排了长队一样,所以最后给人的感觉像是在顺序执行。
其实不然,依次减少SLEEP的时间,最后可以减少成:
TestThread.m_testThreads[i].Start();
Thread.Sleep(0);
而它和
Thread.Sleep(0);
TestThread.m_testThreads[i].Start();
又会有什么区别呢?呵呵。。。。。   回复  引用  查看    

#5楼  2008-07-16 22:34 Seattle      

的确很好,不过我看不大明白哦。
  回复  引用  查看    


标题  
姓名  
主页
Email (只有博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2006-06-19 16:52 编辑过


相关链接: