一、线程理论基础
线程的优点
使用多线程的理由之一:
和进程相比,他是一种非常节俭的多任务操作方式。在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护他的代码段、堆栈段和数据段
 一个进程有多个线程,线程间彼此切换时间很小,一个进程的开销是线程的30倍
使用多线程的理由之二:
线程间方便的通信机制。同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其他线程所使用(局部变量,形参除外)。而进程具有独立的数据空间,要进行数据的传递只能通过进程间通讯方式进行
 其他理由:
 使多cpu系统(多核)更加有效,操作系统会保证当线程数不大于cpu数目时,不同线程运行于不同的cpu上
 改善程序结构,一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改
所需头文件
Linux系统下的多线程遵循POSIX线程接口,称为pthread,编写Linux下的多线程程序需要用到头文件pthread.h,连接时需要使用libpthread.h
 gcc编译时需要加 -lpthread
二、多线程程序设计
创建
#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:线程要执行的函数,为(void*)型
 arg:start_rtn的参数(void*)型,没有的话为NULL
 返回值为0成功
退出
如果进程中任何一个线程中调用exit,或者_exit,那么整个进程都会停止。线程的正常退出方式有:
 (1)线程从启动历程中返回
 (2)线程可以被另一个进程终止
 (3)线程自己可以调用pthread_exit函数终止
 pthread_exit函数
#include<pthread.h>
void pthread_exit(void *rval_ptr)
 
功能:终止调用线程
 Rval_ptr:线程退出返回值的指针
线程等待
int pthread_join(pthread_t tid,void **rval_ptr);
功能:阻塞调用线程,直到指定的线程终止
 tid:等待推出的线程id
 rval_ptr:线程退出的返回值的指针(二级指针),没有则为NULL
 返回值为0,失败;否则返回值
三、线程同步
进行多线程编程,因为无法知道哪个线程会在哪个时候对共享资源进行操作,因此让如何保护共享资源变得复杂
 线程之间的资源竞争:
 1互斥量
 2信号灯
 3条件变量
互斥量
创建
互斥锁不能定义在主函数里面,应定义在全局
 互斥量使用类型为pthread_mutex_t表示。在使用之前需要对其进行初始化;
 对于静态分配的互斥量,可以把它设置为默认的mutex对象PTHREAD_MUTEX_INITIALLIZER
 对于动态分配的互斥量,在申请内存(malloc)之后,通过pthread_mutex_init进行初始化,并且释放内存(free)之前需要调用pthread_mutex_destroy
初始化
#include<pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
 
上锁
对共享资源的访问,要使用互斥量进行加锁,如果互斥量已经上了锁,调用线程会阻塞,直到互斥量被解锁。
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)
 
销毁
pthread_mutex_destroy(pthread_mutex_t *mutex);
 
条件变量
创建
定义变量 pthread_cond_t cond;
条件变量初始化
 a、静态初始化
  PTHREAD_COND_INITIALIZER
 b、动态初始化
  int pthread_cond_init(pthread_cond_t * cond,pthread_condattr_tond_attr);
 
条件变量撤销
int pthread_cond_destroy(pthread_cond_t *cond);
条件变量等待
int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);
发送信号
int pthread_cond_signal(pthread_cond_t *cond);
 int pthread_cond_broadcast(pthread_cond_t *cond);
过程: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);
四、生产者消费者问题
//模拟生产者生产面包和消费者吃面包
//创建两个进程,一个进程生产,一个进程消费
//使用循环队列存放面包
//生产者生产面包,此时线程上锁,消费者不能吃面包,生产完之后解锁,消费者可以吃面包,当生产面包足够(队满)的时候,生产者不生产,等待
//当面包生产足够的时候(队列满),消费者吃面包,此时线程上锁,生产者不得进行生产,直到面包被吃完(队列空)
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<errno.h>
#define BUFFSIZE 16
#define OVER -1
struct procon
{
    int buffer[BUFFSIZE];//存放面包,最多放16个
    int front,rear;//循环队列指针,用来判断空和满
    pthread_mutex_t mutex;//互斥锁
    pthread_cond_t noempty;//条件变量,不空
    pthread_cond_t nofull;//条件变量,不满
};
typedef struct procon P;
P buf;//定义结构体类型的变量
void init_all(P *buf)//初始化
{
    pthread_mutex_init(&buf->mutex,NULL);
    pthread_cond_init(&buf->noempty,NULL);
    pthread_cond_init(&buf->nofull,NULL);
    buf->front = buf->rear = 0;
}
void put_in(P *buf,int data)
{
    //上锁
    pthread_mutex_lock(&buf->mutex);
    //判满的时候
    if ((buf->rear + 1) % BUFFSIZE == buf->front)
    {//释放锁,等待另一个线程给一个没有满的信号,被唤醒之后会拿回锁
        pthread_cond_wait(&buf->nofull,&buf->mutex);
    }
    //没有满的时候,将面包放在货架上
    buf->buffer[buf->rear] = data;
    buf->rear = (buf->rear + 1) % BUFFSIZE;
    pthread_cond_signal(&buf->noempty);
    //解锁
    pthread_mutex_unlock(&buf->mutex);
}
int get_from(P *buf)
{
    int data;
    pthread_mutex_lock(&buf->mutex);
    //空的时候
    if (buf->front == buf->rear)
    {
        pthread_cond_wait(&buf->noempty,&buf->mutex);
    }
    //不空的时候
    data = buf->buffer[buf->front];
    buf->front = (buf->front + 1) % BUFFSIZE;
    pthread_cond_signal(&buf->nofull);
    pthread_mutex_unlock(&buf->mutex);
    return data;
}
void *produce_tid(void)
{
    int i;
    //生产队只生产20个面包
    for ( i = 0; i < 20; i++)
    {
        sleep(rand()%3);//生产时间随机
        printf("%d.one bread is producted\n",i + 1);
        put_in(&buf,i + 1);
    }
    put_in(&buf,OVER);
    return NULL;
}
void *contumer_tid(void)
{
    int d;
    while (1)
    {
        sleep(rand()%3);//吃面包时间随机
        d = get_from(&buf);
        if (d == OVER)
        {
            break;
        }
        printf("%d.eat a bread\n",d);
    }
    return NULL;
}
int main()
{
    pthread_t tid_p,tid_c;//创建两个线程生产者和消费者
    init_all(&buf);//调用函数,初始化互斥锁和条件变量
    //创建线程  
    pthread_create(&tid_p,NULL,(void *)produce_tid,NULL);
    pthread_create(&tid_c,NULL,(void *)contumer_tid,NULL);
    //等待线程结束
    pthread_join(tid_c,NULL);
    pthread_join(tid_p,NULL);
    return 0;
}










