lijgame的专栏

Footprints on the sands of time

原创 多线程学习笔记收藏

新一篇: 解决域名、类名冲突的一个小技巧 | 旧一篇: 以半透明方式混合2张图,例子程序一个

写在前面的一些废话:
   原本是在学习GDI+的,在动手做一个图像处理的小程序时突发奇想,想把很费时的图象处理代码放在一个后台线程中,放在后台线程中后又想把进度通过进度条显示出来,所以就学习了一些多线程方面的知识,发现.net提供了很方便的方法 ,呵呵,方便啊方便,真方便。。。

===================废话好了,正文开始====================

首先一个概念:
1、UI线程:
  大部分的Windows窗体应用程序最终都只有一个线程,所有的UI活动都发生在这个线程上,这个线程通常称为UI线程。
  一句话就是拥有窗体,按钮等等的那个线程。

2、异步委托调用:
.net为我们提供了一个很简单的多线程编程方法,即利用异步委托。调用委托的BeginInvoke方法,我们既可以让一些较耗时的操作在系统线程池的线程中运行,这样就不会阻塞UI线程而导致程序界面没有响应了。这种方式比较简单,我们举一个简单的例子:

//我们先定义一个委托
public delegate void SlowWork_delegate(myclass myArgs);
public void SlowWork(myclass args)
{    
    
//do some slow work
    
//这个函数将在另一个线程中运行
}

//在这个函数中,我们将调用SlowWork函数
//并让它在另一个线程中运行
//这个线程不需要我们维护。
public void callSlowWork()
{
    
//SlowWork函数需要一个myclass类型的参数,我们先创建一个
    myclass mc = new myclass();
    
//创建一个委托
    SlowWork_delegate d = new SlowWork_delegate(SlowWork);

    
//异步调用这个委托,clr会从托管的线程池中获取一个线程来运行SlowWork函数
    
//mc是SlowWork需要的参数,如果需要传多个参数的话我们可以自定义一个结构
    d.BeginInvoke(mc,null,null);
}

这就是一个多线程程序了,简单吧。。。。

但是这个例子比较有局限性,有时我们想知道这个在后台工作的线程工作的怎么样了,最常见的比如说我们在一个后台线程中处理一幅图,我们需要把处理情况反映在一个进度条上,但是我们不能在后台线程中去更新由UI线程创建的控件。比如在上个例子中,我们直接在SlowWork函数中去更新进度条的话会抛出异常的
 

public void SlowWork(myclass args)
{    
    
//do some slow work
    
//这个函数将在另一个线程中运行
    ProgressBar1.Value = aInt; //会产生无法预料的后果
}

 这种情况我们可以利用Control类提供的BeginInvoke方法来实现,看下面的例子:
注意:这里用了一个.net 2.0的新东西:EventHandler泛型委托,说穿了就是由.net预先定义好的一个delegate
只要我们写的函数的签名符合他的类型就能直接用EventHandler了,而不用我们自己定义一个委托了

//先定义一个我们自己的EventArgs类来传递我们需要的数据
public class myEventArgs : EventArgs
{
    
private int _percent;
    Public myEventArgs(
int val)
    
{
            percent 
= val;
    }

    
public int percent
    
{
        
getreturn _percent; }
        
set{ _percent = value; }
    }

}

//这个函数用来更新UI
public void updateUI(object o,myEventArgs args)
{
    ProgressBar1.Value 
= args.percent;
}


//下面这个函数是在另一个线程中运行,所以不能直接调用updateUI函数
public void SlowWork(myclass args)
{    
    
//这个函数将在另一个线程中运行
    
//do some slow work
    
    
//ProgressBar1.Value = aInt; //会产生无法预料的后果
    
//updateUI(null,new myEventArgs(aInt));//显然这样调用和上面的没啥区别

    
//这里才是区别
    
//创建一个updateUI函数的委托
    EventHandler ehandle = new EventHandler(updateUI);

    
//调用Control类的BeginInvoke方法,这里假设我们是在form里写的代码
    
//因为form是继承自Control类的,所以这里就直接调用BeginInvoke方法了
    BeginInvoke(ehandle,new object[]{null,new myEventArgs(aInt)});
}


public void callSlowWork()
{
    
//SlowWork函数需要一个myclass类型的参数,我们先创建一个
    myclass mc = new myclass();
    
//创建一个委托
    SlowWork_delegate d = new SlowWork_delegate(SlowWork);

    
//异步调用这个委托,clr会从托管的线程池中获取一个线程来运行SlowWork函数
    
//mc是SlowWork需要的参数,如果需要传多个参数的话我们可以自定义一个结构
    d.BeginInvoke(mc,null,null);
}

我们可以再修改一下上面的updateUI函数,使其调用更加方便

//这个函数用来更新UI
public void updateUI(object o,myEventArgs args)
{
    
//如果是由其他线程调用的,就采取异步方式
    if(ProgressBar1.InvokeRequired)
    
{
        EventHandler eh 
= new EventHandle(updateUI);
        BeginInvoke(eh,
new object[]{0,args});
    }

    
else    //如果不是其他线程调用的,就直接更新
        ProgressBar1.Value = args.percent;
}

这样,在SlowWork函数中直接调用updateUI函数就可以了,不需要创建委托。

上面已经是一段相对比较完整的代码了,一般的多线程任务都能用以上的方法来解决了^^

有时间我会上传一个完整的程序上来

参考资料:http://www.microsoft.com/china/MSDN/library/enterprisedevelopment/softwaredev/misMultithreading.mspx?mfr=true

发表于 @ 2006年12月21日 15:38:00|评论(loading...)|编辑

新一篇: 解决域名、类名冲突的一个小技巧 | 旧一篇: 以半透明方式混合2张图,例子程序一个

评论:没有评论。

发表评论  


登录
Csdn Blog version 3.1a
Copyright © lijgame