Thinking in c++ -- 并发(1)定义任务,使用线程

Thinking in c++ -- 并发(1)定义任务,使用线程
      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帮你就对了。领导先走很简单,设个优先级就对了。

下一章:共享有限资源。

你可能感兴趣的:(Thinking in c++ -- 并发(1)定义任务,使用线程)