线程
什么是线程?线程的优点是什么?
线程在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;
}