对于一个进程来说,即使没有主动去创建线程,进程也有默认的主线程,线程是项目的实际执行者,根据项目执行计划书,一行行的执行下去

进程好比是一个项目,线程就是将项目需求,拆分为一个个开发任务,将任务拆分出来,拆分的足够细致,就可以加快开发的进度

图片

这样分别更加适合开发

如果将任务分为两个进程去执行,那么会出现因为会议室是独立,而事情不受控制的情况

而且创建进程占用的资源太多,

最终因为进程之间无法传递数据导致无法共享的问题

在一个进程中,一般也不会只有一个主线程,毕竟希望可以管控风险,可以在一些不是很大的中断出现的时候,交给单独的线程去处理这件事

那么如何去创建线程

假如我们有N个视频去下载,一个个下载的时间太长了,我们可以利用多线程,将任务分为多个线程去各自下载

进程的执行是需要项目执行计划书的,线程是一个项目小组,小组有自己的项目执行计划书,就是一个函数,我们将执行的子任务在这个函数里面

函数的参数是void类型的指针,接收任何类型的参数,比如我们将下载文件的文件名进行传递

#include <pthread.h>

#include <stdio.h>

#include <stdlib.h>

#define NUM_OF_TASKS 5

void *downloadfile(void *filename)

{

printf(“I am downloading the file %s!\n”, (char *)filename);

sleep(10);

long downloadtime = rand()%100;

printf(“I finish downloading the file within %d minutes!\n”, downloadtime);

pthread_exit((void *)downloadtime);

}

int main(int argc, char *argv[])

{

char files[NUM_OF_TASKS][20]={“file1.avi”,”file2.rmvb”,”file3.mp4″,”file4.wmv”,”file5.flv”};

pthread_t threads[NUM_OF_TASKS];

int rc;

int t;

int downloadtime;

pthread_attr_t thread_attr;

pthread_attr_init(&thread_attr);

pthread_attr_setdetachstate(&thread_attr,PTHREAD_CREATE_JOINABLE);

for(t=0;t<NUM_OF_TASKS;t++){

printf(“creating thread %d, please help me to download %s\n”, t, files[t]);

rc = pthread_create(&threads[t], &thread_attr, downloadfile, (void *)files[t]);

if (rc){

printf(“ERROR; return code from pthread_create() is %d\n”, rc);

exit(-1);

}

}

pthread_attr_destroy(&thread_attr);

for(t=0;t<NUM_OF_TASKS;t++){

pthread_join(threads[t],(void**)&downloadtime);

printf(“Thread %d downloads the file %s in %d minutes.\n”,t,files[t],downloadtime);

}

pthread_exit(NULL);

}

函数最后调用了pthread_exit函数进行退出线程

函数传入了一个参数为void *类型为线程退出时候的返回值

我们声明一个属性 pthread_attr_t,利用这个属性来进行初始化,并设置PTHREAD_CREATE_JOINABLE,表示等待这个线程结束,获取退出时候的状态

图片

然后是一个循环,对于每个文件和线程,我们创建一个线程,利用函数 pthread_create创建线程,一共有四个参数,第一个是线程对象,然后是线程属性,然后线程运行函数,第四个参数是线程运行函数的参数

任务分配完成,然后等待每个线程下载对应的文件,然后等待这个子任务完成之后,发送信号给其他所有的进程,主线程利用pthread_join获取到下载的消耗时间,告诉主线程

程序完成了,开始编译,多线程程序要依赖于libpthread.so

gcc download.c -lpthread

那么我们就可以进行运行,得到一个普通线程的创建和运行

图片

多线程情况下的数据,基本分为了三类

图片

线程本地数据

函数执行过程中的局部变量,因为是在本地栈上的,所以基本没问题

栈的大小可以利用命令 ulimit -a查看,默认为8MB,可以使用ulimit -s修改

对于线程栈可以修改对应的大小,利用如下的函数

int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);

进程内共享的全局数据,因为是全局共享的,所以需要一种机制保护其,就是锁机制

最后就是线程私有数据,利用函数创建

int pthread_key_create(pthread_key_t *key, void (*destructor)(void*))

这个key被创建了之后,所有的线程都可以进行访问,各个线程都可以key中放入值,提供一个同名不同值的全局变量

int pthread_setspecific(pthread_key_t key, const void *value)

然后是根据key获取到对应的value

void *pthread_getspecific(pthread_key_t key)

我们来说一下数据的保护问题

在Linux中,有对应的互斥工具,Mutex,锁机制

#include <pthread.h>

#include <stdio.h>

#include <stdlib.h>

#define NUM_OF_TASKS 5

int money_of_tom = 100;

int money_of_jerry = 100;

//第一次运行去掉下面这行

pthread_mutex_t g_money_lock;

void *transfer(void *notused)

{

pthread_t tid = pthread_self();

printf(“Thread %u is transfering money!\n”, (unsigned int)tid);

//第一次运行去掉下面这行

pthread_mutex_lock(&g_money_lock);

sleep(rand()%10);

money_of_tom+=10;

sleep(rand()%10);

money_of_jerry-=10;

//第一次运行去掉下面这行

pthread_mutex_unlock(&g_money_lock);

printf(“Thread %u finish transfering money!\n”, (unsigned int)tid);

pthread_exit((void *)0);

}

int main(int argc, char *argv[])

{

pthread_t threads[NUM_OF_TASKS];

int rc;

int t;

//第一次运行去掉下面这行

pthread_mutex_init(&g_money_lock, NULL);

for(t=0;t<NUM_OF_TASKS;t++){

rc = pthread_create(&threads[t], NULL, transfer, NULL);

if (rc){

printf(“ERROR; return code from pthread_create() is %d\n”, rc);

exit(-1);

}

}

for(t=0;t<100;t++){

//第一次运行去掉下面这行

pthread_mutex_lock(&g_money_lock);

printf(“money_of_tom + money_of_jerry = %d\n”, money_of_tom + money_of_jerry);

//第一次运行去掉下面这行

pthread_mutex_unlock(&g_money_lock);

}

//第一次运行去掉下面这行

pthread_mutex_destroy(&g_money_lock);

pthread_exit(NULL);

}

如果没有Mutex相关的行,那么就会出现数据不对的问题

如果有这些Mutex的行,数据必然是正确的

使用mutex,首先要使用 pthread_mutex_init函数初始化这个mutex,初始化之后,就可以保护共享变量了

对应的加锁解锁的函数如下

phtread_mutex_lock(),尝试去加锁,加上了,就继续执行,不然就阻塞

pthread_mutex_trylock(),尝试加锁,加不上,直接返回错误码

释放锁

pthread_mutex_unlock进行释放

最后一个主线程利用pthread_mutex_destory销毁嗦

图片

其次就是根据锁带来的条件变量的问题,就是对应的condition,利用条件变量,可以进行对应线程的唤醒,告诉他,可以获取锁了,但是可以获取锁,不等于一定可以获取到锁

#include <pthread.h>

#include <stdio.h>

#include <stdlib.h>

#define NUM_OF_TASKS 3

#define MAX_TASK_QUEUE 11

char tasklist[MAX_TASK_QUEUE]=”ABCDEFGHIJ”;

int head = 0;

int tail = 0;

int quit = 0;

pthread_mutex_t g_task_lock;

pthread_cond_t g_task_cv;

void *coder(void *notused)

{

pthread_t tid = pthread_self();

while(!quit){

pthread_mutex_lock(&g_task_lock);

while(tail == head){

if(quit){

pthread_mutex_unlock(&g_task_lock);

pthread_exit((void *)0);

}

printf(“No task now! Thread %u is waiting!\n”, (unsigned int)tid);

pthread_cond_wait(&g_task_cv, &g_task_lock);

printf(“Have task now! Thread %u is grabing the task !\n”, (unsigned int)tid);

}

char task = tasklist[head++];

pthread_mutex_unlock(&g_task_lock);

printf(“Thread %u has a task %c now!\n”, (unsigned int)tid, task);

sleep(5);

printf(“Thread %u finish the task %c!\n”, (unsigned int)tid, task);

}

pthread_exit((void *)0);

}

int main(int argc, char *argv[])

{

pthread_t threads[NUM_OF_TASKS];

int rc;

int t;

pthread_mutex_init(&g_task_lock, NULL);

pthread_cond_init(&g_task_cv, NULL);

for(t=0;t<NUM_OF_TASKS;t++){

rc = pthread_create(&threads[t], NULL, coder, NULL);

if (rc){

printf(“ERROR; return code from pthread_create() is %d\n”, rc);

exit(-1);

}

}

sleep(5);

for(t=1;t<=4;t++){

pthread_mutex_lock(&g_task_lock);

tail+=t;

printf(“I am Boss, I assigned %d tasks, I notify all coders!\n”, t);

pthread_cond_broadcast(&g_task_cv);

pthread_mutex_unlock(&g_task_lock);

sleep(20);

}

pthread_mutex_lock(&g_task_lock);

quit = 1;

pthread_cond_broadcast(&g_task_cv);

pthread_mutex_unlock(&g_task_lock);

pthread_mutex_destroy(&g_task_lock);

pthread_cond_destroy(&g_task_cv);

pthread_exit(NULL);

}

看如上的代码

创建了10个任务,每个任务一个字符,另外有两个变量 head/tail

表示工作从哪里开始,到哪里结束

如果head 等于 tail,则说明工作分配完毕,如果tail加N,就是重新分配了N个工作

图片

那么对于每一个员工,就要获取到锁,查看对应的head是否可以获取任务,有了任务就可以拿到任务,进行解锁并工作,

如果head和tail相等,则需要等待,调用 pthread_cond_wait进行等待,这个函数会将锁作为变量传进去,等待的过程中需要解锁,避免阻塞他人

boss也就是主线程,初始化了条件变量和锁,然后创建了是哪个线程

然后分配任务,一共10个,分四批分配,

分配的时候,先获取到锁 pthread_mutex_lock,然后tail加1即可,还需要利用pthread_cond_broadcast进行通知员工

然后进行抢锁,抢到了需要判断head tail是否相同,有了任务,释放锁,然后继续抢,直到任务结束,进入wait状态

整体套路就是这样

我们说了一下如何创建线程,以及线程的数据和线程数据的保护

整体总结如下

图片

发表评论

邮箱地址不会被公开。 必填项已用*标注