Linux多线程编程

       在传统的UNIX模型中,当一个进程需要另一个实体来完成某事,它就fork一个子进程并让子进程去处理。但是fork的调用有如下缺点:

      1fork代价是 昂贵的fork要把父进程的内存印象复制到子进程,并在子进程中复制所有描述符等。

      2fork返回之后父子进程之间信息的传递需要进程通信机制。调用fork之前父进程向尚未存在的子进程传递信息相当容易,因为子进程将从父进程数据空间及所有描述符的一个副本开始运行,但是从子进程向父进程返回信息却比较费力。

      针对这两点,多线程技术相应而生,它具有如下优越性:

      1)它是一种非常"节俭"的多任务操作方式。而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。

      2)线程间方便的通信更加方便。由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。但是,同一进程内的所有线程共享相同的全局内存,这样线程之间的通信就变得相当简单,随之而来的就是同步问题

一、基本线程函数

1.pthread_create函数,创建线程

int pthread_create(pthread_t *tid, const pthread_attr_t *attr, void *(*func)(void *), void *arg);

      pthread_t *tid:一个进程内的各个线程是由线程ID标识的,如果新线程创建成功,返回tid指针

      const pthread_attr_t *attr:每个线程有多个属性,包括优先级、初始栈大小、是否是一个守护线程等等。

      void *(*func)(void *)线程启动函数,线程从调用这个函数开始,或显示结束(调用pthread_exit()),或隐式结束(让该函数返回)。

      void *arg:线程执行func函数的传递参数

2.pthread_join函数,等待一个线程终止

int pthread_join(pthread_t *tid, void **status);

      void **status:二级指针,如果status指针非空,那么所等待线程的返回值将存放在status指向的位置。

3.pthread_self函数,返回线程ID

int pthread_self(void);

      跟进程比较,相当于getpid

4.pthread_detach函数,线程分离

int pthread_detach(pthread_t tid);  

       线程或者是可汇合的joinable),或者是脱离的detach)。当可汇合的线程终止时,线程ID和退出状态将保留,知道另外一个线程调用pthread_join。脱离的线程终止时,释放所有的资源,因此我们不能等待它终止。若要一个线程知道另一个线程的终止时间,我们就要保留第二个线程的可汇合性。

5.pthread_exit函数,线程终止

int pthread_detach(pthread_t tid);  

      若线程未脱离,那么它的线程ID和退出状态将保留到另外一个线程调用pthread_join为止。

二、多线程的同步

      有了上面的基本函数还不足以完成本题的要求,为什么呢?因为题目要求按照ABCABC...的方式打印,而3个线程却在抢占资源,所以无法控制排列顺序。这时就需要用到多线程编程中的同步技术。

      对于多线程编程来说,同步就是同一时间只允许一个线程访问资源,而其他线程不能访问。多线程有3种同步方式:

  • 互斥锁
  • 条件变量
  • 读写锁

2.1 互斥锁

      互斥锁是最基本的同步方式,它用来保护一个临界区,保证任何时刻只由一个线程在执行其中的代码。这个临界区通常是线程的共享数据

      下面三个函数给一个互斥锁上锁和解锁:


int pthread_mutex_lock(pthread_mutex_t *mptr);

int pthread_mutex_trylock(pthread_mutex_t *mptr);

int pthread_mutex_unlock(pthread_mutex_t *mptr);

 

  假设线程2要给已经被线程1锁住的互斥锁(mutex)上锁(即执行pthread_mutex_lock(mutex)),那么它将一直阻塞直到到线程1解锁为止(即释放mutex)。

      如果互斥锁变量时静态分配的,通常初始化为常值PTHREAD_MUTEX_INITIALIZER,如果互斥锁是动态分配的,那么在运行时调用pthread_mutex_init函数来初始化。

2.2 条件变量

      互斥锁用于上锁,而条件变量则用于等待,通常它都会跟互斥锁一起使用。


int pthread_cond_wait(pthread_cond_t *cptr,pthread_mutex_t *mptr);

int pthread_cond_signal(pthread_cond_t *cptr);

 通常pthread_cond_signal只唤醒等待在相应条件变量上的一个线程,若有多个线程需要被唤醒呢,这就要使用下面的函数了:

int pthread_cond_broadcast(pthread_cond_t *cptr);

2.3 读写锁

      互斥锁将试图进入连你姐去的其他简称阻塞住,而读写锁是将读和写作了区分,读写锁的分配规则如下:

      1)只要没有线程持有某个给定的读写锁用于写,那么任意数目的线程可以持有该读写锁用于读;

      2)仅当没有线程持有某个给定的读写锁用于读或用于写时,才能分配该读写锁用于写。

int pthread_rwlock_rdlock(pthread_relock_t *rwptr);

int pthread_rwlock_wrlock(pthread_relock_t *rwptr);

int pthread_rwlock_unlock(pthread_relock_t *rwptr);

三、多线程问题分析

   分析此题:

  1.主线程main创建3个线程tid0tid1tid2

  2.设一个全局变量num,互斥锁mutex保护此临界区保证每次只有一个线程访问num

  3.若抢占到资源的线程tid并不是我们需要的,那么让它阻塞;

  4.若抢占到资源的线程tid正好是我们需要的,那么就打印相应字母;

  5.解锁,唤醒其他两个等待线程;

  6.main函数等待3个线程打印结束才结束。

      代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<error.h>
#include<unistd.h>
#include<pthread.h>
 
int num=0;
 
static pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond=PTHREAD_COND_INITIALIZER;
 
void *func(void *);
 
int main()
{
    pthread_t tid[3];
    int ret=0,i;
    for(i=0;i<3;i++)
        if((ret=pthread_create(&tid[i],NULL,func,(void*)i))!=0)
            printf("create thread_%c error\n",i+'A');
    for(i=0;i<3;i++)
        pthread_join(tid[i],NULL);
    printf("\n");
    return 0;
}
 
void *func(void *argc)
{
    int i;
    for(i=0;i<10;i++)
    {
        pthread_mutex_lock(&mutex);
        while(num!=(int)argc)
            pthread_cond_wait(&cond,&mutex);  //收到条件变量,则继续执行循环
        printf("%c",num+'A');
        num=(num+1)%3;
        pthread_mutex_unlock(&mutex);
        pthread_cond_broadcast(&cond);
    }
    pthread_exit(0);
}

四、死锁问题

       死锁是指两个或两个以上的执行序在执行过程中, 因争夺资源而造成的一种互相等待的现象。例如: 一个线程 T1 已锁定了一个资源 R1, 又想去锁定资源 R2,而此时另一个线程 T2 已锁定了资源 R2,却想去锁定资源R1,两个线程都想得到对方的资源,而不愿释放自己的资源,造成两个线程都在等待,而无法执行的情况,如下图所示。

https://i-blog.csdnimg.cn/blog_migrate/884e61803b1f4741c649fefc420b794f.png

                                                                               图4-1 死锁示意图

 

参考:http://www.cnblogs.com/Rosanna/p/3576715.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值