Linux多线程设计是指基于Linux操作系统的多线程设计,包括多任务程序设计、并发程序设计、网络程序设计、数据共享等。Linux系统中的多线程遵循POSIX线程接口,称为pthread。
1.什么是线程?
线程是进程的实体,是CPU调度和分派的基本单位,它比进程小,可以独立运行。基本上,一个线程并不拥有系统资源,只是拥有一些运行所必需的资源(比如程序计数器、一组寄存器和堆栈),但它可以与属于同一进程的其他线程共享一个进程所拥有的所有资源。
二、什么时候用多线程?当多个任务可以并行执行时,可以为每个任务启动一个线程。
三。pthread_create函数用于创建线程。
# includepthread.h
in thread _ create(pthread _ t * _ _ restrict _ _ new thread,//新创建的线程ID
_ _ constpthread _ attr _ t * _ _ restrict _ _ attr,//thread属性
Void * (* _ _ start_routine) (void *),//新创建的线程将从start_routine开始执行。
Void *__restrict __arg)//执行函数的参数
返回值:成功-0,失败-返回错误号,使用strerror(errno)函数可以获取错误信息。
四。线程的终止。线程从执行函数返回有三种方式,返回值是线程的退出代码。线程被同一个进程中的其他线程取消调用pthread_exit()函数退出。这里不调用Exit,因为线程调用exit函数,会导致线程所在的进程退出。
一个小例子:
启动两个线程,一个线程执行500次全局变量num加1的操作,另一个线程执行500次全局变量减1的操作。
#包含stdio.h
#包含stdlib.h
#include pthread.h
#包括unistd.h
#包含字符串. h
int num=0;
Void *add(void *arg) {//线程执行函数,执行500次加法。
int i=0,tmp
for(;i 500我)
{
tmp=num 1;
num=tmp
printf(添加1,结果为:%dn ,num);
}
return((void *)0);
}
Void *sub(void *arg)//线程执行函数并执行500次减法。
{
int i=0,tmp
for(;i500我)
{
tmp=num-1;
num=tmp
printf(sub-1,结果为:%dn ,num);
}
return((void *)0);
}
int main(int argc,char** argv) {
pthread_t tid1,tid2
int err
void * tret
err=pthread_create(tid1,NULL,add,NULL);//创建一个线程
如果(呃!=0)
{
printf( pthread _ create error:% s n ,strerror(err));
退出(-1);
}
err=pthread_create(tid2,NULL,sub,NULL);
如果(呃!=0)
{
printf( pthread _ create error:% s n ,strerror(err));
退出(-1);
}
err=pthread_join(tid1,tret);//阻塞等待线程id为tid1的线程,直到它退出。
如果(呃!=0)
{
printf(不能与thread1联接:%sn ,strerror(err));
退出(-1);
}
printf(线程1退出代码%dn ,(int)tret);
err=pthread_join(tid2,tret);
如果(呃!=0)
{
printf(不能与thread1联接:%sn ,strerror(err));
退出(-1);
}
printf(线程2退出代码%dn ,(int)tret);
返回0;
}
g编译文件(g main.cpp -o main)。将报告错误“对‘pthread _ create’的未定义引用”。
这个错误的原因是:pthread库不是linux的默认库,所以编译时需要指定libpthread.a库。
解决方案:编译时,添加-lpthread参数。
执行结果:
乍一看,结果是对的。加500倍,减500倍,最后结果是0。但是如果你仔细看所有的输出,你会发现一些奇怪的事情。
这种不协调的原因是两个线程可以修改同一个变量。如果线程1在执行tmp=50 1后被系统中断,那么线程2递减num=50。当线程1恢复时,它正在执行num=tmp=51。正确的结果应该是50。所以多线程修改共享区的时候,应该采用同步的方式。
动词(verb的缩写)线程同步线程同步的三种方式:
1.互斥体互斥体用pthread_mutex_t数据类型表示。
两种方式初始化,第一种:赋值为常量PTHREAD _互斥体_初始值设定项第二种,当互斥量为动态分配是,使用pthread _互斥体_初始化函数进行初始化,使用pthread _互斥体_销毁函数销毁。
# includepthread.h
int pthread _ mutex _ init(pthread _ mutex _ t * _ _ mutex,
_ _常量pthread _ mutex attr _ t * _ _ mutex attr);
int pthread _ mutex _ destroy(pthread _ mutex _ t * _ _ mutex);
返回值:成功-0,失败-错误编号
加解锁
加锁调用pthread_mutex_lock,解锁调用pthread _互斥锁解锁。# includepthread.h
int pthread _ mutex _ lock(pthread _ mutex _ t * _ _ mutex);
int pthread _ mutex _ unlock(pthread _ mutex _ t * _ _ mutex);
使用互斥量修改上一个程序(修改部分用红色标出):
PTHREAD _ MUTEX _ t my lock=PTHREAD _ MUTEX _ INITIALIZER;
void *add(void *arg) {
int i=0,tmp
for(;i 500我)
{
pthread _ mutex _ lock(我的锁);
tmp=num 1;
num=tmp
printf( 1,结果为:%dn ,编号);
pthread _ mutex _ unlock(我的锁);
}
return((void *)0);
}
void *sub(void *arg)
{
int i=0,tmp
for(;i500我)
{
pthread _ mutex _ lock(我的锁);
tmp=num-1;
num=tmp
printf(-1,结果为:%dn ,编号);
pthread _ mutex _ unlock(我的锁);
}
return((void *)0);
}
2、读写锁允许多个线程同时读,只能有一个线程同时写。适用于读的次数远大于写的情况。读写锁初始化:
# includepthread.h
int pthread _ rwlock _ init(pthread _ rwlock _ t * _ _ restrict _ _ rwlock,
_ _ const pthread _ rwlockattr _ t * _ _ restrict
_ _ attr);
int pthread _ rwlock _ destroy(pthread _ rwlock _ t * _ _ rwlock);
返回值:成功- 0,失败-错误编号
加锁,这里分为读加锁和写加锁。
读加锁:
int pthread _ rwlock _ rd lock(pthread _ rwlock _ t * _ _ rwlock)
写加锁
:int pthread _ rwlock _ wrlock(pthread _ rwlock _ t * _ _ rwlock)
解锁用同一个函数
:int pthread _ rwlock _ unlock(pthread _ rwlock _ t * _ _ rwlock)
3、条件变量条件变量用pthread_cond_t数据类型表示。条件变量本身由互斥量保护,所以在改变条件状态前必须锁住互斥量。
条件变量初始化:
第一种,赋值常量COND线程初始化器;第二种,使用pthread_cond_init函数
int pthread _ cond _ init(pthread _ cond _ t * _ _ restrict _ _ cond,
_ _ const pthread _ condattr _ t * _ _ restrict
_ _ cond _ attr);int pthread _ cond _ destroy(pthread _ cond _ t * _ _ cond);
条件等待
使用pthread_cond_wait等待条件为真。
pthread _ cond _ wait(pthread _ cond _ t * _ _ restrict _ _ cond,
pthread _ mutex _ t * _ _ restrict _ _ mutex)
这里需要注意的是,调用pthread_cond_wait传递的互斥量已锁定,pthread_cond_wait将调用线程放入等待条件的线程列表,然后释放互斥量,在pthread_cond_wait返回时,再次锁定互斥量。
唤醒线程
pthread_cond_signal唤醒等待该条件的某个线程,pthread_cond_broadcast唤醒等待该条件的所有线程。
int pthread _ cond _ signal(pthread _ cond _ t * _ _ cond);
int pthread _ cond _ broadcast(pthread _ cond _ t * _ _ cond)
来一个例子,主线程启动四个线程,每个线程有一个参数我(我=生成顺序),无论线程的启动顺序如何,执行顺序只能为,线程0、线程1、线程2、线程3。
#包含标准视频
#包含标准库
#include pthread.h
#包括unistd.h
#包含字符串。h
#定义调试一
int num=0;
PTHREAD _ MUTEX _ t my lock=PTHREAD _ MUTEX _ INITIALIZER;
PTHREAD _ cond _ t qready=PTHREAD _ COND _ INITIALIZER;
void *线程_函数
{
int I=(int)arg;
内部ret
睡眠(5-I);//线程睡眠,然最老师成的线程,最后苏醒
pthread _ mutex _ lock(我的锁);//调用pthread_cond_wait前,必须获得互斥锁
而(我!=数字)
{
#ifdef调试
printf(线程%d正在等待n ,I);
#endif
ret=pthread_cond_wait(qready,我的锁);//该函数把线程放入等待条件的线程列表,然后对互斥锁进行解锁,这两部都是原子操作。并且在pthread_cond_wait返回时,互斥量再次锁住。
if(ret==0)
{
#ifdef调试
printf(线程%d等待成功n ,I);
#endif
}否则
{
#ifdef调试
printf(线程%d等待失败:%sn ,I,strerror(ret));
#endif
}
}
printf(线程%d正在运行n ,I);
num
pthread _ mutex _ unlock(my lock);//解锁
pthread _ cond _ broadcast(qready);//唤醒所有等待该条件的线程
return(void *)0;
}
int main(int argc,char** argv) {
int i=0,err
pthread _ t tid[4];
void * tret
for(;i4;我)
{
err=pthread_create(tid[i],NULL,thread_func,(void *)I);
如果(呃!=0)
{
printf( thread _ create error:% s n ,strerror(err));
退出(-1);
}
}
for(I=0;I 4;我)
{
err=pthread_join(tid[i],tret);
如果(呃!=0)
{
printf(不能与线程%d:%sn 联接,I,strerror(err));
退出(-1);
}
}
返回0;
}
在非调试模式下,执行结果如图所示:
在调试模式下,执行结果如图所示:
在调试模式下,可以看到线程3先唤醒,然后执行pthread_cond_wait(输出线程3等待)。此时互斥体在pthread_cond_wait中解锁,然后进入等待状态。这是线程2锁定互斥体成功,进入pthread_cond_wait(输出线程2等待),类似解锁互斥体,然后进入等待状态。直到线程0,全局变量与线程参数I一致,满足条件,不进入条件等待,输出线程0正在运行。全局变量num执行加1的操作,解锁互斥体,然后唤醒所有等待这个条件的线程。线程3唤醒并输出线程3等待成功。但如果条件不满足,再次执行pthread_cond_wait。这样,满足条件的线程执行,不满足条件的线程等待。