随笔 - 55  文章 - 15  trackbacks - 0
<2024年4月>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

常用链接

留言簿

随笔分类

随笔档案

搜索

  •  

最新评论

阅读排行榜

评论排行榜

      ZThread中,有个抽象基类Runnable,提供了一个公共接口来执行任务:

class Runnable
{
  public:
    virtual void run() = 0;
    vitural ~Runnable(){}
};

      只要从这个类继承,并且重写run()函数,就可以定义一个任务了!!!即具备了多线程的基础。

      但是只是定义了一个任务,它并不具有线程处理的能力。还要推它一把。

      要使用一个线程,必须要先初始化一个线程,并用Runnable* 类型的对象来构造该线程。该线程在构造函数中会自动调用run()函数,此时,main()函数与run()函数齐飞,秋水共长天一色。

请时刻记住,多线程的程序跟普通程序有不同之处,请看下面一段代码

class test : public Runnable
{
int id;
public:
   test(int i = 0) : id(i){}
   void run()
      {
          cout <<" Do Something " << endl;
      }
};
int main()
{
 try{
   forint i = 0; i < 5; i++ )
     Thread t(new test(i));//看这里看这里,小痘痘没有了!!!
    }catch(Synchronization_Exception& e){
     cerr << e.what() << endl;
    }
}

按理说,t是个局部变量,每次循环,t都会被自动释放掉。这里会不会这个线程对象就没有了呢?从结果上来看,答案是,不会!从原理上来看是,当创建了一个Thread对象的时候,相关联的线程会在线程处理系统中自动注册,并保持其活动状态。基于栈的对象被抛弃,但是线程本身还是活在线程处理系统中。

       如果这么说的话,那么这种情况就有点像,t是一个引用,指向线程处理系统中的实际对象。引用被咔嚓掉,实例还在。从结果上看是这样的。我这么解释,是便于理解。

一个线程想做两个任务的话是不行滴,做了个很无聊的实验,想看看线程处理系统中的实际对象是个嘛
Thread t(new test(1));
Thread t(new test(1));
//----------华丽丽的分割线-----------错误,t重定义了//

{
  Thread t(new test(1));
}
{
  Thread t(new test(1));
}
//----------华丽丽的分割线-----------正常,两个线程, 两个不同的任务//

test* tp = new test;// same task
Thread t1(tp);
Thread t2(tp);
//----------华丽丽的分割线-----------非常正常,两个线程完成一个任务// 
以上实验说明,栈上的名字真的不重要!!!

通过多线程,可以创建一个有响应的用户界面。具体程序就不在码了,这里要说的是有两个问题:1. 怎么退出一个线程 2. 线程间通信。
1. 怎么退出?正常退出,强制退出(包括中断),带来的问题是,非正常退出的话资源不不会释放?
2. 线程间通信,发送一个信号,让子线程正常退出。

考虑下面这段代码:
test* tp = new test;
Thread t1(tp);
delete tp;// 这相当于釜底抽薪,太绝了

千万别这么做,除非你跟你的公司有不共戴天之仇。这么做的问题在于,你不知道你的线程正在用tp做多么重要的事情,突然tp就这么没了,线程也会傻在那里。最终导致代码的不稳定。不稳定懂么,公司都需要稳定的代码。
那么想要一个线程停下来,就让它自己决定是否应该停,这时候它会清理现场,释放堆栈之类的,总之,妥妥的。
总结就是:给他一个flag,让他自己决定是否退出,千万别随便决定别人的命运。

使用执行器简化工作
      其实没看出来有什么简化的,就是不用Thread的构造函数了,用一个executor.execute(Runnable*)函数来做,该函数的参数也是一个Runnable类型的指针。语义上比较好理解了吧。还没有理解其真正的涵义。

      执行器每次都要创建线程,比较耗时耗资源。另外一种方法就是一次创建一堆线程,等你要用的时候就从线程池里面去取。该方法用PoolExecutor,示例代码:
#include "zthread/PoolExecutor.h"
..
..
..
PoolExecutor executor(5);
for(int i = 0; i < 5; i++)
executor.execute(new test(i));
..
..
concurrentExecutor 所有的任务用一个线程,当一个任务执行完毕之后,后一个任务才开始执行,示例代码:
#include"zthread/ConcurrentExecutor.h"
//..
ConcurrentExecutor executor;
for(int i = 0; i < 5; i++ )
executor.execute(new test(i));//任务按提交顺序执行,在下一个任务开始之前执行完成
字体越来越小了      肿么回事

让步
yield()告诉CPU, 我做完了要做的事情,你可以让别人跑了。
sleep()告诉CPU,  我累了,睡会觉,你可以让别人先跑。
设置优先级,告诉CPU,我们是有等级的,让大佬先跑!!!代码示例:

#include "zhread/Thread.h"
//..Something else 
Thread high(new test(1));
Thread medium(new test(2));
Thread low(new test(3));
high.setPriority(High);//总舵主
medium.setPriority(Medium);//分舵主
low.setPriority(Low);//成员

哎,有了等级,大家就更有顺序了“让领导先走!!!”

总结:创建一个任务很简单,重写run就对了。让一个线程执行一个任务很简单,构造函数中放个任务指针就对了。想要轻松执行线程很简单,用个executor帮你就对了。领导先走很简单,设个优先级就对了。

下一章:共享有限资源。

posted on 2012-05-15 17:22 Dino-Tech 阅读(492) 评论(1)  编辑 收藏 引用

FeedBack:
# re: Thinking in c++ -- 并发(1)定义任务,使用线程 2012-07-19 15:12 qq675976614
同一个任务不可以被两个线程完成的吧,线程会把它delete掉的 不管它是栈变量还是堆变量  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理