相信对多线程有所了解的人都知道,子线程是不能直接操作winform上的控件的,因为默认的控件是在主线程上生成的,子线程是不能直接访问或者修改的,直接访问或者修改控件属性的话会报错。这个即使在Java上也是这样,Android中也经常被用到的。
这样的话,子线程岂不是没办法访问主线程生成的控件了,当然是否定的,有问题就用解决办法,微软的人更了解这一点,并且有好几种解决办法,主要的思路就是在子线程里,使用delegate代理一个主线程里面的方法。直接看代码:
private void showDateTimeMethod()
{
while (true)
{
//显示当前时间
label1.Text = "当前时间 " + DateTime.Now.ToString();
//线程暂停
Thread.Sleep(1000);
}
}
private void Form1_Load(object sender, EventArgs e)
{
//新建一个线程
Thread showDateTimethread = new Thread(new ThreadStart(showDateTimeMethod));
//该线程为后台线程
showDateTimethread.IsBackground = true;
//线程启动
showDateTimethread.Start();
}
这样写的话,毫无疑问会报错,就是咱们上面说到的情况,子线程直接操作ui元素。我们对这个进行改造一下:
//声明一个委托类型,该委托类型无输入参数和输出参数
public delegate void ProcessDelegate();
//函数引用,label控件显示当前时间,输入参数无,输出参数无,和声明的委托类型形式一致
public void LabelShow()
{
label1.Text = "当前时间 " + DateTime.Now.ToString();
}
然后在线程中实例化一个委托变量,指向这个函数引用。
while (true)
{
//使用命名方法
ProcessDelegate showProcess = new ProcessDelegate(LabelShow);
//调用label的invoke方法
label1.Invoke(showProcess);
//线程暂停
Thread.Sleep(1000);
}
这样的话就能执行成功了。在这里showDateTimethread.IsBackground = true;必须要加上。不知道大家注意没有,如果子线程里有 死循环(有时间必须用循环)或子线程在进行一个阻塞式的操作,如影响队列里的消息,那么不能主线程用什么方式终止子线程都没门,
this.thread.Suspend();
this.thread.Abort()
通通不管用,Abort() 方法也只是建议子线程终止,而不是无条件强行终目,这个不好,比如应用程序要退出,子线程结束不了会一直驻留内存exe都不会退出进程,有什么办法强行终止子线程哈,不要说类似于(不要用死循环,无意义之类的话,那对阻塞式的操作怎么说呢)
这一句的时候把线程阻死了 System.Messaging.Message m = q.Receive();
不管等多少,线程都是死的。
可以试试在退出的时候用
System.Environment.Exit(System.Environment.ExitCode);
Application.Exit();
还用在那些线程开始前,使用它的属性IsBackground,把它设为true,这样在你程序结束的时候,线程也会自动结束。
大家都知道随着linq和ef的流行,一个新的概念出现了,而且目前比较流行,那就是lambda,匿名函数,大家都知道,匿名函数可以用委托进行操作,可以说他是委托初始化的一种新的,便捷的形式,同样的道理,传统的委托必须得声明一个新的方法,才能使用委托,但是匿名函数,可以省去重新声明一个函数,直接看代码:
while (true)
{
//实例化一个委托变量,使用匿名方法构造
ProcessDelegate showProcess = delegate()
{
label1.Text = "当前时间 " + DateTime.Now.ToString();
};
label1.Invoke(showProcess);
//线程暂停
Thread.Sleep(1000);
}
这样一来的话,简单明了,省去很多代码。