自从上次写了数学之美之分形——C++及OpenCV实现Julia集和Mandelbrot集绘制,还有用OpenMP加速你的程序——以分形绘制为例之后,一直耿耿于怀啊,为什么不能自己实现多进程或者线程编程实现程序的加速呢。
终于,OS课程上学会了线程还有进程编程,现在,就以线程为例来实现程序的加速吧!
首先,得介绍下线程,Linux中的线程实际上就是轻量级的进程,CPU调度的时候是以线程调度的,而一个进程可以拥有多个线程,然后统一由内核统一调度。
(注意,Linux早期版本中没有实现线程,比如Linux0.11,只有进程,如果各位有兴趣,不妨试试自己去实现下内核级线程。)
然后是几个主要的线程库函数:
int pthread_attr_init(pthread_attr_t *attr); 用默认值初始化attr指向的pthread_attr_t结构。pthread_attr_t主要定义了创建线程时需要用户提供的属性信息,pthread_create()根据这些信息创建线程。 函数成功时返回0,出错时返回错误号。当然调用之后需要销毁, pthread_attr_destroy即可。 int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); 该函数用来创建一个线程。attr是创建线程时使用的各种属性,由pthread_attr_init()设定。当该线程被调度时会从函数start_routine(一段用户态代码)开始执行。arg做为参数被传递给start_routine。start_routine的原型为: void * start_routine(void *arg); 如果线程创建成功,返回值0,并且把线程的ID值存放在thread中;当创建不成功时会返回一个错误号:EAGAIN表示系统缺乏足够的资源来创建线程,EINVAL表示attr结构中的属性值非法。 当然,也可以用NULL代替attr的初始化,在需要知道attr的信息或者要修改的时候才去用pthread_attr_init函数 void pthread_exit(void *value_ptr);将调用该函数的线程销毁。它没有返回值,因为调用它的线程已经销毁,所以返回值没有任何地方可以“返回”。value_ptr是传给父线程的返回值,父线程调用pthread_join()可得到这个值。这是线程主动终止的唯一方式。 int pthread_join(pthread_t thread, void **value_ptr); 将调用它的线程阻塞,一直等到thread结束为止。其中thread为被等待的线程ID,value_ptr会接收到被等待线程通过pthread_exit()设置的返回值。
具体相信内容以及其他更多函数请看POSIX Threads Programming
然后开始讲解代码,如果没看过前面几篇文章,建议去看下,因为下面的代码就是修改之前的之后提出来讲的:
1.必不可少的头文件
#include <pthread.h>
2.一些结构以及定义
#define THREAD_NUM 4 typedef struct { int start; int end; pthread_t ppid; pthread_attr_t attr_t; }task_struct; task_struct tasks[THREAD_NUM];
void* draw_task(void* arg) { double deltaX = (XMax - XMin) / width; double deltaY = (YMax - YMin) / height; //int max_iterations = 256; double max_size = 4.0; task_struct* t = (task_struct*)arg; for(int row = t->start;row < t->end;row++) { for(int col = 0;col < width;col++) { int color = 0; Complex c,z; z.real = 0; z.img = 0; c.real = XMin + col * deltaX; c.img = YMin + row * deltaY; //z.real = XMin + col * deltaX; //z.img = YMin + row * deltaY; //c.real = 0.285; //c.img = 0.01; while((color < MAX_COLOR) && ((z.img * z.img + z.real * z.real) < max_size)) { double tmp = z.real * z.real - z.img * z.img + c.real; z.img = z.img * z.real * 2 + c.img; z.real = tmp; color++; } if(color == MAX_COLOR) { IMG_8UB(fractal,col,row) = 0; IMG_8UG(fractal,col,row) = 0; IMG_8UR(fractal,col,row) = 0; } else { IMG_8UB(fractal,col,row) = B[color]; IMG_8UG(fractal,col,row) = G[color]; IMG_8UR(fractal,col,row) = R[color]; } //color %= MAX_COLOR; } } pthread_attr_destroy(&(t->attr_t)); pthread_exit(NULL); } void drawPic() { int step = height / THREAD_NUM; for(int i = 0;i < THREAD_NUM;i++) { tasks[i].start = i * step;//分配任务 tasks[i].end = tasks[i].start + step; if(i == THREAD_NUM - 1) tasks[i].end += height % THREAD_NUM; pthread_attr_init(&(tasks[i].attr_t)); if(pthread_create(&(tasks[i].ppid),&(tasks[i].attr_t),draw_task,(void*)&tasks[i]))//开始建立线程 printf("Error create pthread!\n"); } void* status; for(int i = 0;i < THREAD_NUM;i++) { if(!pthread_join(tasks[i].ppid,&status))//阻塞主线程,等待所有的子线程 printf("Thread %ld,exit with status %d\n",tasks[i].ppid,(int)status); else printf("Error when join\n"); } //cvCvtColor(fractal,fractal,CV_HSV2BGR); cvSaveImage(name[fileIndex++],fractal); cvShowImage("Fractal",fractal); cvCopy(fractal,fcopy); //cvWaitKey(0); }
然后,编译:对于线程函数,需要加上库-lpthread
g++ main.cpp -o main `pkg-config opencv --libs --cflags opencv` -lpthread