> 文章列表 > Linux系统应用编程(四)Linux多线程

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

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

本篇文章主要内容:

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

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

一、线程和进程的区别

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

二、Linux多线程

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

在这里插入图片描述

#include <stdio.h>
#include <pthread.h>/* 主线程:遍历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;
}

在这里插入图片描述

2.线程的同步 - 互斥量

  • 经典同步问题:银行储户取钱(C语言版,Java版储户存钱见Java多线程 7.编程练习)
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>/* 银行账户 */
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;
}

在这里插入图片描述

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

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

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

(2)互斥量的使用

在这里插入图片描述

  • 银行储户取款(同步版)
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>/* 银行账户 */
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 <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>/* 银行账户 */
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多线程

在这里插入图片描述