菜鸟学习历程【21】线程

来源:互联网 发布:ios 数组转json字符串 编辑:程序博客网 时间:2024/05/21 08:37

线程

在讨论线程前,我们把进程的相关概念再提一下,以便于与线程的相互比较。

进程:进程是一个具有一定独立功能的程序的一次运行活动,同时也是资源分配的最小单元
进程是程序执行时的一个实例,即它是程序已经执行到某种程度的数据结构的汇集。
从内核的观点看,进程的目的就是担当分配系统资源(CPU时间、内存等)的基本单位。
Linux系统是一个多进程的系统,它的进程之间具有并行性、互不干扰等特点,每个进程都是一个独立的运行单位,拥有各自的权利和责任。其中,各个进程都运行在独立的虚拟地址空间,因此,即使一个进程发生异常,它也不会影响到系统中的其他进程。

线程:是进程的一个执行流,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。
一个进程由几个线程组成(拥有很多相对独立的执行流的用户程序共享应用程序的大部分数据结构),线程与同属一个进程的其他的线程共享进程所拥有的全部资源。通俗的说,一个进程可以有很多线程,但对于一个线程来说,它只能属于一个进程。

下面我们来分析一下进程与线程的区别:

基本单位 稳定性 资源 地址空间 进程 资源分配的最小单元 强健,一个进程崩溃,其他进程依旧可以进行 耗费资源较大,效率低,速度慢 拥有单独的地址空间 线程 CPU调度和分派的基本单位 脆弱,一个线程崩溃,整个进程结束 耗费资源少,效率高,速度快 没有独立的地址空间,同一进程的所有线程共享进程的地址空间

线程的优点

1.运行于一个进程中的多个线程,它们之间使用相同的地址空间,和进程相比,它是一种非常“节俭”的多任务操作方式

2.线程间彼此切换所需的时间远远小于进程间切换所需要的时间

3.线程间方便的通信机制,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。

4.使多CPU系统更加有效

5.改善程序结构

线程的缺点

线程比较脆弱,当一个线程崩溃,相当于整个进程崩溃;


进程的优点

进程拥有独立的地址空间,一个进程崩溃不会对其他进程产生影响,所以多进程的程序要比多线程的程序健壮

进程的缺点

在进程切换时,耗费资源较大,效率要差,对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。


下面,讲解一下线程的创建、回收、取消、分离、同步:

线程的创建

 int pthread_create(pthread_t *restrict thread,const pthread_attr_t *restrict attr,void*(*start_routine)(void*), void *restrict arg);

参数说明:
pthread_t *restrict thread:线程号
const pthread_attr_t *restrict attr:属性、一般为NULL
void*(start_routine)(void):线程函数
void *restrict arg:arg为线程函数的形参,如果没有写NULL

线程的回收

线程中等待回收资源,是一个阻塞函数,同样会妨碍主线程的执行

int pthread_join(pthread_t thread, void **value_ptr);

参数说明:
pthread_t thread:线程号
void **value_ptr:线程退出状态

线程的取消

一个线程取消另一个线程

int pthread_cancel(pthread_t thread);

参数说明:
pthread_t thread:进程号

取消属性(写在被取消的线程函数内):

 int pthread_setcanceltype(int type, int *oldtype);type:    PTHREAD_CANCEL_DISABLE  (不响应取消信号)    PTHREAD_CANCEL_ASYNCHRONOUS  (立即取消)

线程的分离

线程执行后,自行释放(取代join)

int pthread_detach(pthread_t thread);

一般情况下,我们将形参写成:pthread_self(),即可将自身释放

#include <stdio.h>#include <pthread.h>#include <stdlib.h>void delay(){    int i, j;    i = 10000;    while(i > 0)    {        j = 30000;        while(j > 0)        {            j--;        }        i--;    }}void *Mythread1(void *arg){    int oldtype;    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype);       printf("My thread 1\n");    delay();    return (void *)11;}void *Mythread2(void *arg){    pthread_cancel(*(pthread_t *)arg);    printf("My thread 2:%d\n", arg);    pthread_exit((void *)22);}int main(){    pthread_t tid[2];    int ret;    void *retval;    ret = pthread_create(&tid[0], NULL, Mythread1, NULL);    if(0 != ret)    {        perror("pthread_create1");        exit(1);    }    ret = pthread_create(&tid[1], NULL, Mythread2, (void *)tid[0]);    if(0 != ret)    {        perror("pthread_create2");        exit(1);    }    ret = pthread_join(tid[0], &retval);    if(0 == ret)    {        printf("Thread %d exit %d\n", tid[0], (int)retval);    }    ret = pthread_join(tid[1], &retval);    if(0 == ret)    {        printf("Thread %d exit %d\n", tid[1], (int)retval);    }    return 0;}

线程的同步

互斥量(Mutex)、信号灯、条件变量

信号灯与信号量的函数使用一模一样,只是接口名不同,所以不再做说明;

(1)互斥量
定义互斥量(全局)、主线程中初始化(函数、宏)、访问共享资源前加锁、结束时解锁

定义互斥量:

pthread_mutex_t mutex;

初始化:

int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);

参数说明:
pthread_mutex_t *restrict mutex:&mutex
const pthread_mutexattr_t *restrict attr : NULL

或者使用宏定义来初始化:

 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

加锁:

对共享资源的访问, 要使用互斥量进行加锁, 如果互斥量已经上了锁, 调用线程会阻塞, 直到互斥量被解锁。

int pthread_mutex_lock(pthread_mutex_t *mutex);int pthread_mutex_trylock(pthread_mutex_t *mutex);

trylock是非阻塞调用模式, 如果互斥量没被锁住, trylock函数将对互斥量加锁, 并获得对共享资源的访问权限; 如果互斥量被锁住了,trylock函数将不会阻塞等待而直接返回EBUSY, 表示共享资源处于忙状态

解锁:

在操作完成后,必须给互斥量解锁,也就是前面所说的释放。这样其他等待该锁的线程才有机会获得该锁,否则其他线程将会永远阻塞。

int pthread_mutex_unlock(pthread_mutex_t *mutex);

模拟买票情形:

#include <stdio.h>#include <pthread.h>#include <stdlib.h>int tickets = 100;pthread_mutex_t mutex;void delay(){    int i, j;    i = rand() % 10000;    while(i > 0)    {        j = rand() % 30000;        while(j > 0)        {            j--;        }        i--;    }}void *sale(void *arg){    int cur_tickets;    while(1)    {        pthread_mutex_lock(&mutex);        cur_tickets = tickets;        if(cur_tickets <= 0)        {            pthread_mutex_unlock(&mutex);            break;        }        printf("tickets:%d\n", tickets);        delay();        cur_tickets--;        tickets = cur_tickets;        pthread_mutex_unlock(&mutex);    }}int main(){    pthread_t tid[5];    int ret, i;    void *retval;    srand(time(NULL));    ret = pthread_mutex_init(&mutex, NULL);    if(0 != ret)    {        perror("pthread_mutex_init");        exit(1);    }    for (i = 0; i < 5; i++ )    {        ret = pthread_create(&tid[i], NULL, sale, NULL);        if(0 != ret)        {            perror("pthread_create");            exit(1);        }    }    for (i = 0; i < 5; i++ )    {        ret = pthread_join(tid[i], &retval);        if(0 != ret)        {            perror("pthread_join");            exit(1);        }    }    pthread_mutex_destroy(&mutex);    return 0;}

2.条件变量(conditions)

对线程进行条件限制,通俗的说,对某个线程进行限制,导致其阻塞不进行,直到条件不符合,唤醒线程

初始化:

条件变量的初始化也有两种方式,一种函数、一种宏

int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

销毁:

int pthread_cond_destroy(pthread_cond_t *cond);

线程挂起:

int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);

参数说明:
pthread_cond_t *restrict cond:定义的条件变量
pthread_mutex_t *restrict mutex:解锁操作

唤醒:

int pthread_cond_signal(pthread_cond_t *cond);

参数说明:
pthread_cond_t *cond:定义的条件变量

互斥量与信号量

互斥量:保护资源,线程互斥;一次只能有一个访问共享资源;初始值为1;互斥量要由获得锁的线程来释放(谁获得,谁释放)

信号量:可允许多个线程依次有序访问;初始值为0或1,取决于是计算信号量还是二值信号量;可以由其它线程释放