1.线程
- 线程
线程( thread )技术早在60年代就被提出,但真正应用多线程到操作系统中去,是在80年代中期,solaris是这方面的佼佼者.传统的Unix也支持线程的概念,但是在一个进程( process )中只允许有一个线程,这样多线程就意味着多进程.现在,多线程技术已经被许多操作系统所支持,包括Wind ows/NT、Linux
- 线程的优点
使用多线程的理由之一是:和进程相比,它是一种非常“节俭”的多任务操作方式.在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种"昂贵"的多任务工作方式
运行于一个进程中的多个线程,它们之间使用相同的地址空间,而且线程间彼此切换所需的时间也远远小于进程间切换所需要的时间.据统计,一个进程的开销大约是一个线程开销的30倍左右
使用多线程的理由之二是:线程间方便的通信机制。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过进程间通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便
除了以上所说的优点外,多线程程序作为一种多任务、并发的工作方式,有如下优点:使多 CPU 系统更加有效 . 操作系统会保证当线程数不大于 CPU 数目时,不同的线程运行于不同的 CPU 上 .改善程序结构 . 一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改 .
2.多线程
- 创建
#include <pthread.h>
int pthread_create(pthread_t * tidp,const pthread_attr_t*attr,void*(*start_rtn)(void),void*arg)
tidp:线程id ,输出参数
attr: 线程属性(通常为空,NULL)
start_rtn:线程要执行的函数
arg:start_rtn的参数
#include <pthread.h> int main() { pthread_t tid;// int ret; ret = pthread_create(&tid,NULL,pthread_test1,(void *)message);//pthread_test1线程要执行的函数,message传给线程要执行的函数的参数,需要转换成(void*)类型。也可以为NULL if(ret != 0)//错误判断 { perror("pthread create fail\n"); } }
- 终止线程
如果进程中任何一个线程中调用 exit 或 _exit ,那么整个进程都会终止。线程的正常退出方式有:(1) 线程从启动例程中返回
(2) 线程可以被另一个进程终止
(3) 线程自己调用pthread_exit函数
♣#include <pthread.h>♠void pthread_exit(void * rval_ptr)
功能:终止调用线程Rval_ptr : 线程退出返回值的指针
- 线程等待
#include < pthread.h >int pthread_join(pthread_t tid,void **rval_ptr)
功能:阻塞调用线程,直到指定的线程终止。卐T id :等待退出的线程id☃Rval_ptr:线程退出的返回值的指针♌#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <string.h> char message[] = "hello word"; void * pthread_test1(void *arg) { printf("%s",(char *)arg); strcpy(message,"ni hao"); sleep(5); return NULL; } int main(int argc, const char *argv[]) { pthread_t tid; void * val_ptr; int ret; ret = pthread_create(&tid,NULL,pthread_test1,(void *)message); if(ret != 0) { perror("pthread creat fail!\n"); exit(-1); } printf("wating for pthread\n"); printf("%s\n",message); ret = pthread_join(tid,&val_ptr);//阻塞主线程,等待工作进程结束 if(ret == -1) { perror("join error\n"); } printf("\n"); printf("%s\n",message); return 0; }
3.线程同步
- 线程同步
进行多线程编程,因为无法知道哪个线程会在哪个时候对共享资源进行操作,因此让如何保护共享资源变得复杂,通过下面这些技术的使用,可以解决线程之间对资源的竞争:1 互斥量Mutex
2 信号灯Semaphore
3 条件变量Conditions
- 互斥量
为什么需要互斥量:
Item * p =queue_list;
Queue_list=queue_list->next;
process_job(p);
free(p);
当线程1处理完Item *p=queue_list后,系统停止线程1的运行,改而运行线程2。线程2照样取出头节点,然后进行处理,最后释放了该节点。过了段时间,线程1重新得到运行。而这个时候,p所指向的节点已经被线程2释放掉,而线程1对此毫无知晓。他会接着运行process_job(p)。而这将导致无法预料的后果!
对于这种情况,系统给我们提供了互斥 量.线程在取出头节点前必须要等待互斥量,如果此时有其他线程已经获得该互斥量,那么该线程将会阻塞在这里.只有等到其他线程释放掉该互斥量后,该线程才有可能得到该互斥量。互斥量从本质上说就是一把锁, 提供对共享资源的保护访问
- 创建
在 Linux 中 , 互斥量使用类型 pthread_mutex_t 表示 . 在使用前 , 要对它进行初始化 :
对于静态分配的互斥量, 可以把它设置为默认的mutex对象PTHREAD_MUTEX_INITIALIZER
对于动态分配的互斥量, 在申请内存(malloc)之后, 通过pthread_mutex_init进行初始化, 并且在释放内存(free)前需要调用pthread_mutex_destroy
#include < pthread.h >int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr)
const pthread_m
utexattr_t *attr:一般写NULL
int pthread_mutex_destroy(pthread_mutex_t *mutex)
pthread_mutex_t mutex;//宏定义锁 int main() { pthread_mutex_init(&mutex,NULL);//初始化锁 }
- 加锁
对共享资源的访问 , 要使用互斥量进行加锁 , 如果互斥量已经上了锁 , 调用线程会阻塞 , 直到互斥量被解锁。int pthread_mutex_lock(pthread_mutex_t *mutex)
int pthread_mutex_trylock(pthread_mutex_t *mutex)
返回值 : 成功则返回 0, 出错则返回错误编号。trylock是非阻塞调用模式, 如果互斥量没被锁住, trylock函数将对互斥量加锁, 并获得对共享资源的访问权限; 如果互斥量被锁住了,trylock函数将不会阻塞等待而直接返回EBUSY, 表示共享资源处于忙状态
- 解锁
在操作完成后,必须给互斥量解锁,也就是前面所说的释放。这样其他等待该锁的线程才有机会获得该锁,否则其他线程将会永远阻塞。int pthread_mutex_unlock ( pthread_mutex_t *mutex)
#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> #include <errno.h> int ticket = 10; pthread_mutex_t mutex; void * sell(void) { int ret; while(1) { ret = pthread_mutex_trylock(&mutex); if(ret != EBUSY) { if(ticket > 0) { sleep(1); printf("sell ticket----> chool ticket:%d\n",--ticket); pthread_mutex_unlock(&mutex); } } else { pthread_mutex_unlock(&mutex); break; } pthread_mutex_unlock(&mutex); } return NULL; } int main(int argc, const char *argv[]) { pthread_t tid1,tid2,tid3; int ret = 0; ret = pthread_create(&tid1,NULL,(void *)sell,NULL); if(ret != 0) { perror("pthread create error!\n"); } ret = pthread_create(&tid2,NULL,(void *)sell,NULL); if(ret != 0) { perror("pthread create error!\n"); } ret = pthread_create(&tid3,NULL,(void *)sell,NULL); if(ret != 0) { perror("pthread create error!\n"); } pthread_join(tid1,NULL); printf("tid1 exit!\n"); pthread_join(tid2,NULL); printf("tid1 exit!\n"); pthread_join(tid3,NULL); printf("tid1 exit!\n"); printf("\n"); return 0; }
4.条件变量
- 创建条件变量
pthread_cond_t cond;
- 条件变量初始化
a、静态初始化
PTHREAD_COND_INITIALIZER
b、动态初始化
int pthread_cond_init(
pthread_cond_t * cond,
pthread_condattr_t * cond_attr);
- 条件变量撤销
int pthread_cond_destroy(
pthread_cond_t *cond);
- 条件变量等待
int pthread_cond_wait(
pthread_cond_t * cond,
pthread_mutex_t *mutex);
unlock -> wait->lock
- 示例
线程1
pthread_mutex_lock(&mutex);
if(条件不成立)
pthread_cond_wait(&cond,*mutex);
修改条件
pthread_mutex_unlock(&mutex);
线程2
pthread_mutex_lock(&mutex);
使条件满足
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
- int pthread_cond_signal(pthread_cond_t *cond);//发信号