Linux系统应用编程(四)Linux多线程

本篇文章主要内容:

    • Linux系统应用编程(四)Linux多线程
    • 一、线程和进程的区别
    • 二、Linux多线程
      • 1.线程的使用 - 创建、退出、等待
      • 2.线程的同步 - 互斥量
        • (1)互斥量的理解(略)
        • (2)互斥量的使用
        • (3)死锁
      • 3.线程间通信- 条件变量
        • (1)条件变量的理解
        • (2)条件变量的使用

Linux系统应用编程(四)Linux多线程

一、线程和进程的区别

  • 进程是静态的程序/代码,在操作系统分配的资源下运行起来用于完成特定任务的动态程序,简单说就是代码在操作系统上跑起来/运行起来就是一个进程。这个过程中,操作系统会为进程分配独立的地址空间,每个进程之间相互独立;
  • 线程是进程的一条执行路径,只有独立的堆栈和局部变量,没有独立的地址空间,且线程共享进程的资源。可以说,进程是操作系统分配资源的基本单元,线程是进程执行过程中的一个单元。
  • 线程相比于进程,线程之间实现通信,共享数据更方便,不需要像进程间通信使用额外的机制;线程运行切换速度快,开销小,比进程的响应速度快,更轻量级。

二、Linux多线程

1.线程的使用 - 创建、退出、等待

Linux系统应用编程(四)Linux多线程_第1张图片

#include 
#include 

/* 主线程:遍历100内奇数 */
/* 子线程:遍历100内偶数 */
/* 主线程等待子线程退出后,输出其退出返回值 */

void *threadRun(void *pretn){
    static int i = 0;
    for(i=0;i<=100;i++){
        if(i%2 == 0)printf("subThread:%d\n",i);
    }
    pthread_exit(pretn);
}

int main(){

    pthread_t thread1;
    int arg = 2023;
    void *ptemp = &arg;
    pthread_create(&thread1,NULL,threadRun,ptemp);

    for(int i=0;i<=100;i++){
        if(i%2 != 0)printf("Main:%d\n",i);
    }
    void *p = NULL;
    pthread_join(thread1,&p);
    printf("subThread return value: %d\n",*(int *)p);
    return 0;
}

Linux系统应用编程(四)Linux多线程_第2张图片

2.线程的同步 - 互斥量

  • 经典同步问题:银行储户取钱(C语言版,Java版储户存钱见Java多线程 7.编程练习)
#include 
#include 
#include 
#include 

/* 银行账户 */
typedef struct Account {
    int balance;		//余额
    int (*dramMoney)(int *,int);	//取钱方法
    int (*saveMoney)(int *,int);	//存钱方法(本例未实现)
}Account;


/* 取钱方法实现 */
int dramMoney(int *bal,int cash){
    while(*bal>0 && cash>0){	//余额,取钱金额大于0,可以取钱
        usleep(20*1000);	//由于未进行同步,即使进行条件判断,也会出现错误
        *bal -= cash;		//取钱:余额=余额-取出的金额
        printf("(tid=%ld)Dram Money:%d$ succeed,now balance:%d$\n",pthread_self(),cash,*bal);
    }
    return *bal;
}

/* 客户取钱(一个线程=一个客户) */
void *customer(void *acc){
    Account *ptmp =  (Account *)acc;
    ptmp->dramMoney(&ptmp->balance,1000);
    pthread_exit(acc);
}

int main(){
	
    /* 初始化:账户里有18000 */
    Account *acc = (Account *)malloc(sizeof(Account));
    acc->balance = 18000;
    acc->dramMoney = dramMoney;

    /* 创建两个线程模拟两个客户同时取钱 */
    pthread_t t1,t2;
    pthread_create(&t1,NULL,customer,acc);
    pthread_create(&t2,NULL,customer,acc);

    pthread_join(t1,NULL);
    pthread_join(t2,NULL);
    return 0;
}

Linux系统应用编程(四)Linux多线程_第3张图片

  • 可以看到,出现了线程安全问题。接下来使用互斥量对其进行修改

(1)互斥量的理解(略)

互斥量(mutex)本质上是一把锁,在访问共享资源前对互斥量进行加锁,访问完成后释放锁。加锁状态下,其他线程阻塞等待,防止多个线程同时访问相同的共享资源。

(2)互斥量的使用

Linux系统应用编程(四)Linux多线程_第4张图片

  • 银行储户取款(同步版)
#include 
#include 
#include 
#include 

/* 银行账户 */
typedef struct Account {
    int balance;		//余额
    int (*dramMoney)(int *,int);	//取钱方法
    int (*saveMoney)(int *,int);	//存钱方法(本例未实现)
}Account;

pthread_mutex_t mutex;

/* 取钱方法实现 */
int dramMoney(int *bal,int cash){
    while(1){	//余额,取钱金额大于0,可以取钱
        pthread_mutex_lock(&mutex);
        if(*bal > 0) {			//如果未进行同步,即使进行条件判断,也会出现错误
            usleep(50 * 1000);
            *bal -= cash;        //取钱:余额=余额-取出的金额
            printf("(tid=%ld)Dram Money:%d$ succeed,now balance:%d$\n", pthread_self(), cash, *bal);
        }else{
            pthread_mutex_unlock(&mutex);
            break;
        }
        pthread_mutex_unlock(&mutex);
    }
    return *bal;
}

/* 客户取钱(一个线程=一个客户) */
void *customer(void *acc){
    Account *ptmp =  (Account *)acc;
    ptmp->dramMoney(&ptmp->balance,1000);
    pthread_exit(acc);
}

int main(){
    pthread_mutex_init(&mutex,NULL);
    /* 初始化:账户里有180000 */
    Account *acc = (Account *)malloc(sizeof(Account));
    acc->balance = 180000;	
    acc->dramMoney = dramMoney;

    /* 创建两个线程模拟两个客户同时取钱 */
    pthread_t t1,t2;
    pthread_create(&t1,NULL,customer,acc);
    pthread_create(&t2,NULL,customer,acc);

    pthread_join(t1,NULL);
    pthread_join(t2,NULL);
    pthread_mutex_destroy(&mutex);
    return 0;
}

PS:原先设置账户余额1800,发现cpu速度太快第二个线程还没抢到锁运行就结束,改成18万就能看到线程切换了 如果是ubuntu虚拟机,把cpu设置成1核就可以看到线程切换运行了,前面发现只有单线程再跑,还以为是代码有问题

(3)死锁

死锁是指在多进程或多线程下,由于进程/线程之间竞争获取共享资源,导致进程/线程间彼此都相互等待对方释放所持有的资源,使程序所有线程处于无法继续执行的无限等待状态。<如何避免死锁?>规划设计好代码中线程对同步锁的操作,避免嵌套同步、也尽量减少同步资源的定义以避免出现死锁。

3.线程间通信- 条件变量

(1)条件变量的理解

​ 条件变量是Linux线程同步和线程通信的一种机制,条件变量可以使线程进入等待,也可以唤醒等待中的进程,以确定何时执行某些操作,例如等待某个资源的可用性;条件变量和互斥量配合使用,可以实现线程同步(线程安全)的线程间通信,也可以解决某个线程长时间得不到锁处于等待状态(线程饥饿)。例如:在多线程环境中实现线程安全的线程间通信,避免同一时间的并发访问共享资源。

(2)条件变量的使用

用法上很像Java的wait()、notify()实现线程间通信的方法。

  • 银行储户取钱(使用条件变量+互斥量实现两个储户交替取钱)
#include 
#include 
#include 
#include 

/* 银行账户 */
typedef struct Account {
    int balance;		//余额
    int (*dramMoney)(int *,int);	//取钱方法
    int (*saveMoney)(int *,int);	//存钱方法(本例未实现)
}Account;

pthread_mutex_t mutex;
pthread_cond_t cond;

/* 取钱方法实现 */
int dramMoney(int *bal,int cash){
    while(1){	//余额,取钱金额大于0,可以取钱
        pthread_mutex_lock(&mutex);
        pthread_cond_signal(&cond);
        if(*bal > 0) {
            usleep(50 * 1000);    //由于未进行同步,即使进行条件判断,也会出现错误
            *bal -= cash;        //取钱:余额=余额-取出的金额
            printf("(tid=%ld)Dram Money:%d$ succeed,now balance:%d$\n", pthread_self(), cash, *bal);

            /* 取完钱等待并释放锁给另一个线程;另一个线程得到锁就上锁再唤醒它,依次循环实现交替执行 */
            pthread_cond_wait(&cond,&mutex);	
            pthread_mutex_unlock(&mutex);
        }else{
            pthread_mutex_unlock(&mutex);
            break;
        }
    }
    return *bal;
}

/* 客户取钱(一个线程=一个客户) */
void *customer(void *acc){
    Account *ptmp =  (Account *)acc;
    ptmp->dramMoney(&ptmp->balance,1000);
    pthread_exit(acc);
}

int main(){
    pthread_mutex_init(&mutex,NULL);
    pthread_cond_init(&cond,NULL);
    /* 初始化:账户里有90000 */
    Account *acc = (Account *)malloc(sizeof(Account));
    acc->balance = 9000;
    acc->dramMoney = dramMoney;

    /* 创建两个线程模拟两个客户同时取钱 */
    pthread_t t1,t2;
    pthread_create(&t1,NULL,customer,acc);
    pthread_create(&t2,NULL,customer,acc);

    pthread_join(t1,NULL);
    pthread_join(t2,NULL);
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
    return 0;
}

Linux系统应用编程(四)Linux多线程_第5张图片

在这里插入图片描述

你可能感兴趣的:(#,linux,c语言,经验分享,嵌入式硬件,开发语言)