什么是线程?线程的优点是什么? 线程在Unix下,被称为轻量级的进程,线程虽然不是进程,但可以看作是Unix进程的表亲,同一进程中的多条线程共享该进程中的全部资源,如虚拟地址空间、文件描述符、和信号处理等等,但同一进程中的多个线程有各自的调用栈(call satck),自己的寄存器环境(register context),自己的线程本地存储(thread-local storage)。一个进程可以有很多线程,每条线程执行不同的任务。 线程可以提高应用程序在多核环境下处理诸如文件I/O或者socket I/O等会产生堵塞的情况的表现性能。在Unix系统中,一个进程包含很多东西,包括可执行的程序,以及一大堆诸如文件描述符地址空间等资源。在很多情况下,完成相关任务的不同代码间需要交换数据。如果采用多进程的方式,那么**通信就需要在用户空间和内核空间进程频繁的切换,开销很大,**但是如果使用多线程的方式,因为可以使用共享的全局变量,所以线程间的通信(数据交换)变得非常高效。 线程得创建,等待,退出
头文件:
#include<pthread.h>函数:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); int pthread_join(pthread_t thread, void **retval); void pthread_exit(void *retval);在多线程运行得环境中,如果多个线程都去访问和操作同一个地址或者变量,会造成出乎意料得结果,所以呢,线程之间就需要进行互斥和同步。有两种形式提供了线程之间得互斥和同步,锁和信号量,我们先看锁。
:顾名思义,就是把门加固,让任何人或者动物无法进入门内,也就是说当一个线程在对某个地址或者变量进行访问时,加一把锁,这个时候,其他得线程就不能访问该地址或变量了,只有等该线程把锁打开了,也就是释放了,其他线程申请到锁,才能继续访问。
//在主线程中初始化锁为解锁状态 pthread_mutex_t mutex; pthread_mutex_init(&mutex,NULL); //在编译时初始化锁为解锁状态: //锁初始化 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //访问对象时的加锁操作与解锁操作 //加锁: pthread_mutex_lock(&mutex); //释放锁 pthread_mutex_unlock(&mutex);下面看看一个简单得例子,使用多线程和互斥锁来模拟火车站窗口售票
#include<stdio.h> #include<stdlib.h> #include<string.h> #include <semaphore.h> #include<pthread.h> #define N 32 pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER; #define THREAD 5 typedef struct ticket { int count; char saleticketname[N]; }TICKET; typedef struct data { TICKET *data; char threadname[N]; }THREADDATA; void *saleticket(void *arg) { THREADDATA *data = (THREADDATA *)arg; while(data->data->count>0) { pthread_mutex_lock(&g_mutex); printf("%s出售了,第%d张%s,",data->threadname,data->data->count--,data->data->saleticketname); if(data->data->count>=0) { printf("出票成功,剩余%d张票\n",data->data->count); } else { printf("出票失败\n"); exit(-1); } pthread_mutex_unlock(&g_mutex); sleep(1); } return NULL; } int main(void) { int i=0; TICKET data; data.count=100; pthread_t tid1,tid2,tid3,tid4,tid5; THREADDATA thdata[THREAD]; strcpy(data.saleticketname,"北京---邯郸"); // pthread_mutex_init(&g_mutex,0); thdata[i].data=&data; memset(thdata[i].threadname,0,N); sprintf(thdata[i].threadname,"窗口%d",i); pthread_mutex_lock(&g_mutex); pthread_create(&tid1,NULL,saleticket,&thdata[i]); printf("%s开始出售%s车票\n",thdata[i].threadname,data.saleticketname); pthread_mutex_unlock(&g_mutex); i++; thdata[i].data=&data; memset(thdata[i].threadname,0,N); sprintf(thdata[i].threadname,"窗口%d",i); pthread_mutex_lock(&g_mutex); pthread_create(&tid2,NULL,saleticket,&thdata[i]); printf("%s开始出售%s车票\n",thdata[i].threadname,data.saleticketname); pthread_mutex_unlock(&g_mutex); i++; thdata[i].data=&data; memset(thdata[i].threadname,0,N); sprintf(thdata[i].threadname,"窗口%d",i); pthread_mutex_lock(&g_mutex); pthread_create(&tid3,NULL,saleticket,&thdata[i]); printf("%s开始出售%s车票\n",thdata[i].threadname,data.saleticketname); pthread_mutex_unlock(&g_mutex); i++; thdata[i].data=&data; memset(thdata[i].threadname,0,N); sprintf(thdata[i].threadname,"窗口%d",i); pthread_mutex_lock(&g_mutex); pthread_create(&tid4,NULL,saleticket,&thdata[i]); printf("%s开始出售%s车票\n",thdata[i].threadname,data.saleticketname); pthread_mutex_unlock(&g_mutex); i++; thdata[i].data=&data; memset(thdata[i].threadname,0,N); sprintf(thdata[i].threadname,"窗口%d",i); pthread_mutex_lock(&g_mutex); pthread_create(&tid5,NULL,saleticket,&thdata[i]); printf("%s开始出售%s车票\n",thdata[i].threadname,data.saleticketname); pthread_mutex_unlock(&g_mutex); pthread_join(tid5,NULL); pthread_join(tid4,NULL); pthread_join(tid3,NULL); pthread_join(tid2,NULL); pthread_join(tid1,NULL); system("pause"); return 0; }看看执行结果 可以看到,窗口的顺序不是按顺序来执行的,这个是操作系统去调度的,但是没有出现重复售票的行为,这只是一个简单的例程,真正的火车票售票系统比这个复杂多了。
信号量本质上是一个非负数的整数计数器,也被用来控制对公共资源的访问。当公共资源增加的时候,调用信号量增加函数**sem_post()对其进行增加,当公共资源减少的时候,调用函数sem_wait()**来减少信号量。
头文件:
semaphore.h信号量的数据结构为sem_t,本质上是一个long型整数.
函数:
//初始化信号量 int sem_init(sem_t *sem,int pshared,unsigned int value); //成功返回0,失败返回-1 //参数: //sem:指向信号量结构的一个指针 //pshared:不是0的时候,该信号量在线程间共享,否则只能为当前进程的所有线程门共享 //value:信号量的初始值 //信号量减一操作,当sem=0的时候该函数会堵塞 int sem_wait(sem_t *sem); //成功返回0,失败返回-1 //参数 //sem:指向信号量的一个指针 //信号量的加一操作 int sem_post(sem_t *sem); //成功返回0,失败返回-1 //参数 //sem:指向信号量的一个指针下面就看一个简单的小例子,创建两个线程,一个线程负责接受终端的输入信息,另一个线程负责把从终端接收到的信息输出出来。
#include<stdio.h> #include<stdlib.h> #include <string.h> #include <semaphore.h> #include<pthread.h> sem_t g_sem1,g_sem2; #define N 256 char g_buf[N]; void *driver(void *arg) { while(1) { sem_wait(&g_sem1); printf("output--->%s\n",g_buf); sem_post(&g_sem2); if(strncmp(g_buf,"quit",4)==0) { break; } } return NULL; } void *sailer(void *arg) { while(1) { sem_wait(&g_sem2); printf("please input-->\n"); fgets(g_buf,N,stdin); sem_post(&g_sem1); if(strncmp(g_buf,"quit",4)==0) { break; } } return NULL; } int main(void) { pthread_t tid1,tid2; int ret; sem_init(&g_sem1,0,0); sem_init(&g_sem2,0,1); ret=pthread_create(&tid1,NULL,driver,NULL); if(ret!=0) { printf("线程1创建失败\n"); return -1; } ret=pthread_create(&tid2,NULL,sailer,NULL); if(ret!=0) { printf("线程2创建失败\n"); return -1; } pthread_join(tid1,NULL); pthread_join(tid2,NULL); return 0; }