这是我的C#线程一章的作业,先同时运行第一、二号线程,都完了之后,才运行第三号线程。但是写到两个线程都完了之后,我就不知道怎样写返回主线程了的代码了。
这种窗口程序又和控制台程序不一样,控制台程序返回到main()方法就可以了,但这种我就不知道支线程完了之后,返回到主线程是返回到哪里去了。开始还以为返回到“frmThreadTest”(即主form)中,不对;返回到按钮点击事件中,也不对。真不知如何是好。
而且两个支线程有可能不是同时结束的,所以本来下面三句代码可以在回到主线程之后才写,结果因为不知道怎么回到主线程,要分别写在两个方法下面,导致代码冗余。
dt2 = DateTime.Now;
lblEnd.Text = dt2.ToLongTimeString() + "." + dt2.Millisecond;
        lblUsed.Text = (dt2 - dt1).ToString();
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;namespace ThreadTest
{
    public partial class frmThreadTest : Form
    {
        Thread t1,t2;
        Random r;
DateTime dt2;
DateTime dt1;        public frmThreadTest()
        {
            InitializeComponent();
            Control.CheckForIllegalCrossThreadCalls = false; 
            int seed = DateTime.Now.Millisecond;
            r = new Random(seed);
        }        private void btnExit_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }        public void RunLabel(Label lbl)
        {
lbl.Text = r.Next(0, 10).ToString();
        }        public void RunLabel1()
        {
             for (int i = 0; i < 100; i++)
{
RunLabel(lbl1);
                 Thread.Sleep(100);
}
dt2 = DateTime.Now;
lblEnd.Text = dt2.ToLongTimeString() + "." + dt2.Millisecond;
             lblUsed.Text = (dt2 - dt1).ToString();
             t1.Join();
        }        public void RunLabel2()
        {
             for (int i = 0; i < 100; i++)
{
RunLabel(lbl2);
                 Thread.Sleep(100);
}
dt2 = DateTime.Now;
lblEnd.Text = dt2.ToLongTimeString() + "." + dt2.Millisecond;
             lblUsed.Text = (dt2 - dt1).ToString();
             t2.Join();
        }
        
        private void btnStart_Click(object sender, EventArgs e)
{
dt1 = DateTime.Now;
lblStart.Text = dt1.ToLongTimeString() + "." + dt1.Millisecond;
lblEnd.Text = "";
             lblUsed.Text = "共耗时:";
             t1 = new Thread(new ThreadStart(RunLabel1));
             t2 = new Thread(new ThreadStart(RunLabel2));
             t1.Start();
             t2.Start();
        }        private void btnStop_Click(object sender, EventArgs e)
        {
                t1.Abort();
                t2.Abort();
        } private void frmThreadTest_Load(object sender, EventArgs e)
{}
    }
}

解决方案 »

  1.   

    啊?!这样?
    那么可不可以一运行支线程,就让主线程的代码先停下,然后支线程的代码都运行完了,再运行主线程的代码呢?
    而且,我实在想不到在窗口程序环境(相对控制台程序)下,主线程是怎样接收(接手)支线程的?是通过一个方法还是一个类?
    我在按钮事件和form启动事件里面写都不行。
      

  2.   

    如果主线程停下来,给人的感觉像窗体好像死机了一样。主线程可以通过线程的IsAlive属性判断线程是否结束
      

  3.   

        private void btnStart_Click(object sender, EventArgs e)
        {
            dt1 = DateTime.Now;
            lblStart.Text = dt1.ToLongTimeString() + "." + dt1.Millisecond;
            lblEnd.Text = "";
            lblUsed.Text = "共耗时:";  
            t1 = new Thread(new ThreadStart(RunLabel1));
            t2 = new Thread(new ThreadStart(RunLabel2));
            t1.Start();
            t2.Start();        while(t1.IsAlive || t2.IsAlive);
            //可以加这里
        }
      

  4.   

    回上面几位同学:
    join方法也用了,信号变量也设了。问题是支线程一运行完,不论是Alive或join还是abort,都好像不知道主线程在哪里,总之就停在线程调用的方法的末尾,不动了。
    根本去不到信号变量的判断语句的位置(我是放在btnStart事件那里,也放过在form_load事件里,都不行),所以也运行不下去。
      

  5.   

    你可以设置几个boolean变量来判断某一条线程是否结束
      

  6.   

    while(true)
    {
       sleep(300);
       if (!th1.Alive)
       {
         th2.stat();
         return;
       }
    }
      

  7.   

    定义一个变量
    比如 int nowtype= 0;然后1,2,线程运行完后
    nowtype ++;在主线程里
    thread1.Start();
    thread2.Start();while(nowtype<2)
    {}
    thread3.Start();建议你把这几个start单独放到一个线程里.然后在btn点进行加载
      

  8.   

    先感谢15楼和16楼的同学,但是,我在13楼时也写了,信号变量也设过了,确实也是写在1、2线程后面的。
    但是,15楼和16楼的“在主线程”里的代码,到底是写在什么地方合适呢?
    我曾经在按钮事件btnStart_Click和窗体导入事件frmThreadTest_Load里写上这些代码,但支线程就是去到线程末尾的时候停下了。
    所以请问16楼nealbox,“主线程”到底是在哪里呢?
      

  9.   

    使用回调方法如下public partial class Form1 : Form {
      public Form1 () {
      }  private btnStart_Click(object sender, EventArgs e) {
        Thread t1 = new Thread(new ThreadStarter(Thread1_Proc));
        Thread t2 = new Thread(new ThreadStarter(Thread2_Proc));
        t1.Start();
        t2.Start();
      }  private void OnThreadFinish(int threadID) {
        //...... run in ui thread
        Label1.Text += threadID.ToString()+" finished!";
      }  private delegate void DelegateOnThreadFinish(int);  private void Thread1_Proc() {
        //... do work
        this.Invoke(new DelegateOnThreadFinish(OnThreadFinish), new object[]{1}); // call OnThreadFinished 
      }
      private void Thread2_Proc() {
        // ... do work 
        this.Invoke(new DelegateOnThreadFinish(OnThreadFinish), new object[]{2});  }
    }
      

  10.   

    汗死,自然是保持异步
    新建一个类
    class NewThread()
    {
    public void main(){
    thread1=new Thread(new ThreadStart(first));
    thread2=new Thread(new ThreadStart(second));
    thread3=new Thread(new ThreadStart(third));
    thread newt = Thread(new ThreadStart(beginthread));
    newt.Start();
    }
    private void beginthread()
    {
    thread1.Start(); 
    thread2.Start(); while(nowtype <2) 
    { } 
    thread3.Start(); 
    }
    }在first和second 里把全局变量nowtype ++
    把主线程和保持运行等待的线程分开
    不用写的再全了吧?
      

  11.   

    根据amylee200808 的思路,我把代码修改一下。
    确实可以先运行完了一二线程,然后再运行第三件工作了,但我发现TA用的是又开一个线程,然后把停止了的一二线程归并到这个新线程里,所以其实一二线程结束之后,并没有回到主线程,而只是去到这个支线程而已。于是我把TA的代码稍微改了一下,不开一个新线程,而直接建一个方法,然后归并一二线程、然后开展之后的任务等等,都在这个方法里面完成就是了。就是把下面两行代码:
    Thread TestT = new Thread(new ThreadStart(Test)); 
    Test.Start();
    直接改成Test();
    直接运行就行了。但所有控件改变了值之后,下一句都要加上.Update();否则控件改变不能产生作用。
    非常感谢amylee200808,因为是TA给了我思路。
    做到这里,我又突然想起,其实我在学校做这个作业的时候,已经问过我的老师,但老师有事赶时间,只说了一句:你建一个方法来收集这两个线程嘛。但当时我脑子不知道怎的堵住了,总是想不通:支线程完了之后,怎么知道要自己跑去那个方法里面去呢?
    只是这个功课花费的时间太多了,其他同学的代码,我就没有一个一个试了。
    谢谢各位同学。
      

  12.   

    原本以为问题已经解决了,刚刚无意中又试了一下,才发现,用我自己修改了amylee200808代码的方法(即不新建一个线程,直接建一个方法去启动一二线程,然后再Jion()回它们),出现了问题。
    原来界面上的一个用于测试停止线程的按钮,居然不能产生作用了。即下面几行代码。
        private   void   btnExit_Click(object   sender,   EventArgs   e) 
        { 
             Application.Exit(); 
        } 
    然后用回amylee200808的建立线程的方法,按钮又能用了。
    看来真是欲罢不能,可能要试试其他高手的代码了。