Linux-线程相关操作

什么是线程?


在一个程序里的一个执行路线就叫做线程,线程是进程的一个实体,是CPU调度和分配的基本单位,它是比进程更小的可以独立运行的基本单位。一切进程至少都有一个线程。

线程与进程

进程是资源竞争的基本单位
线程是程序执行的最小单位
一个线程可以与所属进程内其他线程的共享进程资源
线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器,栈,线程ID,errno,信号屏蔽字,调度优先级)


线程相关函数

功能:创建一个线程
原型:int pthread_create(pthread_t *thread,const pthread_attr_t *attr,
void*(*start_routine)(void*),void* arg)
参数:
    thread:返回线程ID
    attr:设置线程属性,为NULL时表示默认属性
    start_routine:是个函数地址,线程启动后要执行的函数
    arg:传给线程启动函数的参数
返回值:
     成功返回0,失败返回错误码


线程终止

  如果需要终止某个线程而不终止整个进程可以有三种方法
  1 从线程函数return,但对主线程不适用,从main函数return相当于调用exit。
  2 线程可以调用函数pthrea_exit终结自己。
  3 一个线程可以调用pthread_cancel函数来终止同一进程中的其他线程。
线程终止函数
原型:void pthread_exit(void* value_ptr)
参数:
    value_ptr:不要指向一个局部变量
返回值:
    无返回值,线程结束时无法返回到它的调用者
原型:int pthread_cancel(pthread_t thread);
功能:取消一个执行中的进程
参数:
    thread:线程ID
返回值:
     成功返回0,失败返回错误码
注意:需要注意,pthread_exit或者return返回的指针所指向的内存单元必须是全
局 的或者是⽤malloc分配的,不能在线程函数的栈上分配


线程等待

    为啥子需要进程等待
    1 线程退出后,其空间地址没有释放,仍然在进程的地址空间内
    创建的新线程不会使用刚才退出线程的地址空间(类似与僵尸进程,占用的资源
    在进程结束前都不会被回收,所以一个线程结束后我们应该等待回收其资源)
    2 可以防止新创建的线程还未结束,整个进程就结束退出 
功能:等待线程结束
原型:int pthread_join(pthread_t thread, void **value_ptr);
参数:
    thread:线程ID
    value_ptr:它指向⼀个指针,后者指向线程的返回值
返回值:
    成功返回0;失败返回错误码

例:用两个线程来对全局变量进行加减

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

//pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;

int num=0;
void *add(void*arg){
    int i=0;
    int tmp;
    for(;i<500;i++){
        //pthread_mutex_lock(&mutex);    
        tmp=num+1;
        num=tmp;
        printf("add+1 result is:%d\n",num);
        //pthread_mutex_unlock(&mutex);
    }
   return ((void *)0);
     // return 0;
}//线程执行函数加500次操作

void * sub(void* arg){

    int i=0;
    int tmp;
    for(;i<500;i++){
        //pthread_mutex_lock(&mutex);
        tmp=num-1;
        num=tmp;
        printf("sub-1 result is:%d\n",num);
       // pthread_mutex_unlock(&mutex);
    }

    return ((void *)0);
     // return 0;
}
//线程执行函数减500次操作

int main(int argc, char** argv){
    pthread_t tid1,tid2;
    void* ret;
    int err;
    if(pthread_create(&tid1,NULL,add,NULL)!=0){
        printf("create error\n");
        exit(-1);
    }

    if(pthread_create(&tid2,NULL,sub,NULL)!=0){

        printf("create errr\n");
        exit(-1);

    }
    if((err=pthread_join(tid1,&ret))!=0){
        printf("join error:%s\n",strerror(err));
        exit(-1);
    }
    printf("thread 1 exit code %d\n",(int)ret);
    if((err=pthread_join(tid2,&ret))!=0){
        printf("join error:%s\n",strerror(err));
        exit(-1);
    }
    printf("thread 2 exit code %d\n",(int)ret);

    return 0;
}

注意:如果编译时报错undefined reference to `pthread_create’
原因是:pthread库不是linux默认的库,所以在编译时候需要指明libpthread.a库。
解决方法:在编译时,加上-lpthread参数。
这里写图片描述

运行后我们发现了一点不和谐的东东,这是由于多个线程并发操作共享变量带来的问题。为了解决多线程的访问冲突问题我们可以引入互斥锁机制。获得锁的线程可以完成读写修改操作,然后释放锁给其他线程,没有获得锁的线程无法对共享数据进行操作,这样“读-修改-写”三步操作组成一个原子操作,要么都执行,要么都不执行,不会执行到中间被打断,也不会在其它处理器上并行做这个操作。


线程同步与互斥

互斥量的初始化
静态分配:
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
动态分配:
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
参数:
 mutex:要初始化的互斥量
 attr:NULL
互斥锁的销毁
int pthread_mutex_destory(pthread_mutex*mutex);
销毁互斥量需要注意:
使用PTHREAD_ MUTEX_ INITIALIZER初始化的互斥量不需要销毁
不要销毁一个已经加锁的互斥量
已经销毁的互斥量,要确保后⾯不会有线程再尝试加锁
互斥量加锁和解锁
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
返回值:成功返回0,失败返回错误号
注意:发起函数调用时,其他线程已经锁定互斥量,或者存在其他线程
同时申请互斥量,但没有竞争到互斥量,那么pthread_ lock调用
会陷入阻塞,等待互斥量解锁。


利用互斥量修改上述程序后

pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;

int num=0;
void *add(void*arg){
    int i=0;
    int tmp;
    for(;i<500;i++){
        pthread_mutex_lock(&mutex);    
        tmp=num+1;
        num=tmp;
        printf("add+1 result is:%d\n",num);
        pthread_mutex_unlock(&mutex);
    }
   return ((void *)0);
     // return 0;
}//线程执行函数加500次操作

void * sub(void* arg){

    int i=0;
    int tmp;
    for(;i<500;i++){
        pthread_mutex_lock(&mutex);
        tmp=num-1;
        num=tmp;
        printf("sub-1 result is:%d\n",num);
        pthread_mutex_unlock(&mutex);
    }

    return ((void *)0);
     // return 0;
}

这里写图片描述


条件变量

初始化
int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr);
参数:
 cond:要初始化的条件变量
 attr:NULL
销毁
int pthread_cond_destroy(pthread_cond_t *cond)
等待条件满足
int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);
参数:
 cond:要在这个条件变量上等待
 mutex:互斥量
唤醒等待
 int pthread_cond_broadcast(pthread_cond_t *cond);
 int pthread_cond_signal(pthread_cond_t *cond);
 pthread_cond_signal唤醒等待该条件的某个线程,pthread_cond_broadcast唤醒等待该条件的所有线程。


实现按一定次序启动线程

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

pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond=PTHREAD_COND_INITIALIZER;

int num=0;
void* pthread_func(void* arg){

    int i=(int)arg;
    int ret;
    pthread_mutex_lock(&mutex);
   while(i!=num){
   //if(i!=num){

        printf("thread %d wait\n",i);

        ret=pthread_cond_wait(&cond,&mutex);
        if(ret==0)
        {
            printf("thread %d wait success\n",i);
        }else {
            printf("thread %d wait fail\n",i); 
        }

    }
    printf("thread %d is runing\n",i);
    num++;
    pthread_mutex_unlock(&mutex);
    pthread_cond_broadcast(&cond);
    return 0;

}



int main()
{
    int i=0;
    int err;
    pthread_t tid[4];
    void *tret;
    for(;i<4;i++){

    if(err=pthread_create(&tid[i],NULL,pthread_func,(void*)i)!=0){
      printf("pthread_creadte error:%s\n",strerror(err));
      exit(-1);
      }

   }
    for(i=0;i<4;i++){

       if(err=pthread_join(tid[i],&tret)!=0){

           printf("pthread_join error %d%s\n",i,strerror(err));
           exit(-1);
       }

    }
    return 0;
 }

运行结果:
这里写图片描述


为什么pthread_cond_wait需要互斥量?

   设想一下如果只有一个线程等待,由于条件不满足,所以它就会一直等,
   一直等肯定不是个事,所以我们需要一个线程来改变原来共享变量的条
   件,使条件得到满足,并且通知原来等待在条件变量上的线程

   想要修改条件就必须修改共享数据,所以就必须要用互斥锁来保护,没
   有互斥锁就无法安全的获取和修改共享数据。

猜你喜欢

转载自blog.csdn.net/important_/article/details/79950198