linux-多线程

一、什么是线程?

线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立执行的基本单位。线程自己基本上不拥有系统资源,仅仅拥有一点在执行中不可缺少的资源(如程序计数器,一组寄存器和栈),可是它可与同属一个进程的其它的线程共享进程所拥有的所有资源。

二、什么时候使用多线程?

当多个任务能够并行运行时,能够为每一个任务启动一个线程。

三、线程的创建

使用pthread_create函数。

#include<pthread.h>
int pthread_create (pthread_t *__restrict __newthread,//新创建的线程ID
			   __const pthread_attr_t *__restrict __attr,//线程属性
			   void *(*__start_routine) (void *),//新创建的线程从start_routine開始运行
			   void *__restrict __arg)//运行函数的參数

返回值:成功-0,失败-返回错误编号,能够用strerror(errno)函数得到错误信息

四、线程的终止

三种方式

  • 线程从运行函数返回,返回值是线程的退出码
  • 线程被同一进程的其它线程取消
  • 调用pthread_exit()函数退出。这里不是调用exit,由于线程调用exit函数,会导致线程所在的进程退出。

一个小样例:

启动两个线程,一个线程对全局变量num运行加1操作,运行五百次,一个线程对全局变量运行减1操作,相同运行五百次。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>

int num=0;
void *add(void *arg) {//线程运行函数,运行500次加法
    int i = 0,tmp;
    for (; i <500; i++)
    {
        tmp=num+1;
        num=tmp;
        printf("add+1,result is:%d\n",num);
    }
    return ((void *)0);
}
void *sub(void *arg)//线程运行函数,运行500次减法
{
    int i=0,tmp;
    for(;i<500;i++)
    {
        tmp=num-1;
        num=tmp;
        printf("sub-1,result is:%d\n",num);
    }
    return ((void *)0);
}
int main(int argc, char** argv) {

    pthread_t tid1,tid2;
    int err;
    void *tret;
    err=pthread_create(&tid1,NULL,add,NULL);//创建线程
    if(err!=0)
    {
        printf("pthread_create error:%s\n",strerror(err));
        exit(-1);
    }
    err=pthread_create(&tid2,NULL,sub,NULL);
    if(err!=0)
    {
        printf("pthread_create error:%s\n",strerror(err));
         exit(-1);
    }
    err=pthread_join(tid1,&tret);//堵塞等待线程id为tid1的线程,直到该线程退出
    if(err!=0)
    {
        printf("can not join with thread1:%s\n",strerror(err));
        exit(-1);
    }
    printf("thread 1 exit code %d\n",(int)tret);
    err=pthread_join(tid2,&tret);
    if(err!=0)
    {
        printf("can not join with thread1:%s\n",strerror(err));
        exit(-1);
    }
    printf("thread 2 exit code %d\n",(int)tret);
    return 0;
}

使用g++编译该文件(g++ main.cpp -o main)。此时会报错undefined reference to `pthread_create‘。

报这个错误的原因是:pthread库不是linux默认的库,所以在编译时候须要指明libpthread.a库。

解决方法:在编译时,加上-lpthread參数。

运行结果:

乍一看,结果是对的,加500次,减500次,最后结果为0。可是细致看全部的输出,你会发现有异样的东西。

导致这个不和谐出现的原因是,两个线程能够对同一变量进行改动。假如线程1运行tmp=50+1后,被系统中断,此时线程2对num=50运行了减一操作,当线程1恢复,在运行num=tmp=51。而正确结果应为50。所以当多个线程对共享区域进行改动时,应该採用同步的方式。

五、线程同步

线程同步的三种方式:

1、相互排斥量

相互排斥量用pthread_mutex_t数据类型来表示。

两种方式初始化,第一种:赋值为常量PTHREAD_MUTEX_INITIALIZER;另外一种,当相互排斥量为动态分配是,使用pthread_mutex_init函数进行初始化,使用pthread_mutex_destroy函数销毁。

#include<pthread.h>
int pthread_mutex_init (pthread_mutex_t *__mutex,
			       __const pthread_mutexattr_t *__mutexattr);
int pthread_mutex_destroy (pthread_mutex_t *__mutex);

返回值:成功-0,失败-错误编号

加解锁

加锁调用pthread_mutex_lock,解锁调用pthread_mutex_unlock。

#include<pthread.h>
int pthread_mutex_lock (pthread_mutex_t *__mutex);
int pthread_mutex_unlock (pthread_mutex_t *__mutex);

使用相互排斥量改动上一个程序(改动部分用红色标出):

pthread_mutex_t mylock=PTHREAD_MUTEX_INITIALIZER;

void *add(void *arg) {

int i = 0,tmp;

for (; i <500; i++)

{

pthread_mutex_lock(&mylock);

tmp=num+1;

num=tmp;

printf("+1,result is:%d\n",num);

pthread_mutex_unlock(&mylock);

}

return ((void *)0);

}

void *sub(void *arg)

{

int i=0,tmp;

for(;i<500;i++)

{

pthread_mutex_lock(&mylock);

tmp=num-1;

num=tmp;

printf("-1,result is:%d\n",num);

pthread_mutex_unlock(&mylock);

}

return ((void *)0);

}

2、读写锁

同意多个线程同一时候读,仅仅能有一个线程同一时候写。适用于读的次数远大于写的情况。

读写锁初始化:

#include<pthread.h>
int pthread_rwlock_init (pthread_rwlock_t *__restrict __rwlock,
				__const pthread_rwlockattr_t *__restrict
				__attr);
int pthread_rwlock_destroy (pthread_rwlock_t *__rwlock);

返回值:成功--0,失败-错误编号

加锁,这里分为读加锁和写加锁。

读加锁:

int pthread_rwlock_rdlock (pthread_rwlock_t *__rwlock)

写加锁

int pthread_rwlock_wrlock (pthread_rwlock_t *__rwlock)

解锁用同一个函数

int pthread_rwlock_unlock (pthread_rwlock_t *__rwlock)

3、条件变量

条件变量用pthread_cond_t数据类型表示。

条件变量本身由相互排斥量保护,所以在改变条件状态前必须锁住相互排斥量。

条件变量初始化:

第一种,赋值常量PTHREAD_COND_INITIALIZER;另外一种,使用pthread_cond_init函数

int pthread_cond_init (pthread_cond_t *__restrict __cond,
			      __const pthread_condattr_t *__restrict
			      __cond_attr);
int pthread_cond_destroy (pthread_cond_t *__cond);

条件等待

使用pthread_cond_wait等待条件为真。

 pthread_cond_wait (pthread_cond_t *__restrict __cond,
			      pthread_mutex_t *__restrict __mutex)

这里须要注意的是,调用pthread_cond_wait传递的相互排斥量已锁定,pthread_cond_wait将调用线程放入等待条件的线程列表,然后释放相互排斥量,在pthread_cond_wait返回时,再次锁定相互排斥量。

唤醒线程

pthread_cond_signal唤醒等待该条件的某个线程,pthread_cond_broadcast唤醒等待该条件的全部线程。

int pthread_cond_signal (pthread_cond_t *__cond);

int pthread_cond_broadcast (pthread_cond_t *__cond)

来一个样例,主线程启动4个线程,每一个线程有一个參数i(i=生成顺序),不管线程的启动顺序怎样,运行顺序仅仅能为,线程0、线程1、线程2、线程3。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#define DEBUG 1

int num=0;
pthread_mutex_t mylock=PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t qready=PTHREAD_COND_INITIALIZER;
void * thread_func(void *arg)
{
    int i=(int)arg;
    int ret;
    sleep(5-i);//线程睡眠,然最先生成的线程,最后苏醒
    pthread_mutex_lock(&mylock);//调用pthread_cond_wait前,必须获得相互排斥锁
    while(i!=num)
    {
#ifdef DEBUG
        printf("thread %d waiting\n",i);
#endif
        ret=pthread_cond_wait(&qready,&mylock);//该函数把线程放入等待条件的线程列表,然后对相互排斥锁进行解锁,这两部都是原子操作。而且在pthread_cond_wait返回时,相互排斥量再次锁住。
        if(ret==0)
        {
#ifdef DEBUG
            printf("thread %d wait success\n",i);
#endif
        }else
        {
#ifdef DEBUG
            printf("thread %d wait failed:%s\n",i,strerror(ret));
#endif
        }
    }
    printf("thread %d is running \n",i);
    num++;
    pthread_mutex_unlock(&mylock);//解锁
    pthread_cond_broadcast(&qready);//唤醒等待该条件的全部线程
    return (void *)0;
}
int main(int argc, char** argv) {

    int i=0,err;
    pthread_t tid[4];
    void *tret;
    for(;i<4;i++)
    {
        err=pthread_create(&tid[i],NULL,thread_func,(void *)i);
        if(err!=0)
        {
            printf("thread_create error:%s\n",strerror(err));
            exit(-1);
        }
    }
    for (i = 0; i < 4; i++)
    {
        err = pthread_join(tid[i], &tret);
        if (err != 0)
        {
            printf("can not join with thread %d:%s\n", i,strerror(err));
            exit(-1);
        }
    }
    return 0;
}

在非DEBUG模式,运行结果如图所看到的:

在DEBUG模式,运行结果如图所看到的:

在DEBUG模式能够看出,线程3先被唤醒,然后运行pthread_cond_wait(输出thread 3 waiting),此时在pthread_cond_wait中先解锁相互排斥量,然后进入等待状态。这是thread 2加锁相互排斥量成功,进入pthread_cond_wait(输出thread 2 waiting) ,相同解锁相互排斥量,然后进入等待状态。直到线程0,全局变量与线程參数i一致,满足条件,不进入条件等待,输出thread 0 is running。全局变量num运行加1操作,解锁相互排斥量,然后唤醒全部等待该条件的线程。thread
3 被唤醒,输出thread 3 wait success。可是不满足条件,再次运行pthread_cond_wait。如此运行下去,满足条件的线程运行,不满足条件的线程等待。

linux-多线程,布布扣,bubuko.com

时间: 2024-08-08 13:51:54

linux-多线程的相关文章

《Linux多线程编程手册》读书笔记

第二章 基本线程编程 1.(P25)如果多个线程等待同一个线程终止,则所有等待线程将一直等到目标线程终止.然后,一个等待线程成功返回,其余的等待线程将失败并返回ESRCH错误. 2.(P26)将新线程的pbe参数作为栈参数进行传递.这个线程参数之所以能够作为栈参数传递,是因为主线程会等待辅助线程终止.不过,首选方法是使用malloc从堆分配存储,而不是传递指向线程栈存储的地址.如果将该参数作为地址传递到线程栈存储,则该地址可能无效或者在线程终止时会被重新分配. 3.(P28)pthread_de

linux 多线程信号处理总结

linux 多线程信号总结(一) 1. 在多线程环境下,产生的信号是传递给整个进程的,一般而言,所有线程都有机会收到这个信号,进程在收到信号的的线程上下文执行信号处理函数,具体是哪个线程执行的难以获知.也就是说,信号会随机发个该进程的一个线程. 2 signal函数BSD/Linux的实现并不在信号处理函数调用时,恢复信号的处理为默认,而是在信号处理时阻塞此信号,直到信号处理函数返回.其他实现可能在调用信号处理函数时,恢复信号的处理为默认方式,因而需要在信号处理函数中重建信号处理函数为我们定义的

Linux多线程程序设计------创建线程

1.创建线程 #include<pthread.h> int pthread_create(pthread_t* tidp,const pthread_attr_t *attr,void*(*start_rtn)(void),void*arg) tidp:线程id attr:线程属性(通常为空) start_rtn:线程要执行的函数 arg:start_rtn的参数 Linux多线程程序设计------创建线程,布布扣,bubuko.com

linux多线程示例

1 #include <stdio.h> 2 #include <unistd.h> 3 #include <stdlib.h> 4 #include <pthread.h> 5 6 typedef void* (*fun)(void*); 7 8 fun fun1, fun2; 9 10 pthread_mutex_t pmu = PTHREAD_MUTEX_INITIALIZER; 11 pthread_cond_t cond; 12 pthread_t

Linux多线程与同步

Linux多线程与同步 作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 典型的UNIX系统都支持一个进程创建多个线程(thread).在Linux进程基础中提到,Linux以进程为单位组织操作,Linux中的线程也都基于进程.尽管实现方式有异于其它的UNIX系统,但Linux的多线程在逻辑和使用上与真正的多线程并没有差别. 多线程 我们先来看一下什么是多线程.在Linux从程序到进程中,我们看到了一个程序在内存中的表示.这个程

Linux多线程编程-互斥锁

互斥锁 多线程编程中,(多线程编程)可以用互斥锁(也称互斥量)可以用来保护关键代码段,以确保其独占式的访问,这有点像二进制信号量.POSIX互斥锁相关函数主要有以下5个: #include <pthread.h> int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr); int pthread_mutex_destroy(pthread_mutex_t *mutex); int p

Linux多线程编程-条件变量

条件变量 如果说线程间的互斥锁是用来同步共享数据的访问的话,那么条件变量是用于线程之间共享数据的值.条件变量提供了一种线程之间的通知机制,当某个共享数据达到某个值时,唤醒等待这个共享数据的线程.条件变量相关函数主要 有5个: #include <pthread.h> int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr); int pthread_cond_destroy(pthread_

Linux多线程

1. Linux多线程概述 1.1. 概述 进程是系统中程序执行和资源分配的基本单位.每个进程有自己的数据段.代码段和堆栈段.这就造成进程在进行切换等操作时都需要有比较负责的上下文切换等动作.为了进一步减少处理器的空转时间支持多处理器和减少上下文切换开销,也就出现了线程. 线程通常叫做轻量级进程.线程是在共享内存空间中并发执行的多道执行路径,是一个更加接近于执行体的概念,拥有独立的执行序列,是进程的基本调度单元,每个进程至少都有一个main线程.它与同进程中的其他线程共享进程空间{堆 代码 数据

【Qt入门实践】Qt之哲学家问题(linux 多线程)

转载请注明出处:http://blog.csdn.net/feng1790291543 linux多线程实现哲学家问题,依据哲学家吃饭.拿筷子.放下筷子...... 首先,主线程使用Qt下的GUI的简单一个button控制即可了 maindesttop.cpp #include "maindesttop.h" #include "ui_maindesttop.h" QMutex mutex01; QMutex mutex02; QMutex mutex03; QMu

Linux多线程编程初探

Linux 线程介绍 进程与线程 典型的UNIX/Linux进程可以看成只有一个控制线程:一个进程在同一时刻只做一件事情.有了多个控制线程后,在程序设计时可以把进程设计成在同一时刻做不止一件事,每个线程各自处理独立的任务. 进程是程序执行时的一个实例,是担当分配系统资源(CPU时间.内存等)的基本单位.在面向线程设计的系统中,进程本身不是基本运行单位,而是线程的容器.程序本身只是指令.数据及其组织形式的描述,进程才是程序(那些指令和数据)的真正运行实例. 线程是操作系统能够进行运算调度的最小单位