informasi kontak saya
Surat[email protected]
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Thread adalah unit eksekusi dalam suatu proses.
Bertanggung jawab atas pelaksanaan program dalam proses saat ini,
Setidaknya ada satu thread dalam suatu proses
Mungkin ada banyak thread dalam satu proses
Beberapa thread berbagi semua sumber daya dari proses yang sama, dan setiap thread berpartisipasi dalam penjadwalan terpadu sistem operasi
Secara sederhana dapat dipahami sebagai proses = sumber daya memori + thread utama + thread anak +…
Untuk tugas-tugas yang terkait erat, multi-threading lebih disukai selama konkurensi. Untuk tugas-tugas yang tidak terkait erat dan relatif independen, disarankan untuk memilih multi-proses;
Perbedaan antara thread dan proses:
Ada banyak perintah untuk melihat proses di sistem Linux, antara lain pidstat, top, ps, Anda dapat melihat prosesnya, Anda juga dapat melihat a
thread sedang dalam proses
Setelah alat sysstat perlu diinstal di ubuntu, pidstat dapat didukung.
sudo apt install sysstat
Pilihan
-t: Menampilkan thread yang terkait dengan proses yang ditentukan
-p: Tentukan pid proses
Contoh
Lihat thread yang terkait dengan proses 12345
sudo pidstat -t -p 12345
Lihat thread yang terkait dengan semua proses
sudo pidstat -t
Lihat utas yang terkait dengan proses 12345, keluaran setiap 1 detik
sudo pidstat -t -p 12345 1
Lihat utas yang terkait dengan semua proses, keluaran setiap 1 detik
sudo pidstat -t 1
Gunakan perintah top untuk melihat thread dalam proses tertentu. Anda perlu menggunakan opsi -H yang dikombinasikan dengan -p untuk menentukan pid.
Pilihan
-H: Menampilkan informasi utas
-p: Tentukan pid proses
Contoh
Lihat thread yang terkait dengan proses 12345
sudo atas -H -p 12345
Lihat thread yang terkait dengan semua proses
sudo atas -H
Gunakan perintah ps yang dikombinasikan dengan opsi -T untuk melihat semua thread dalam suatu proses
Pilihan
-T: Menampilkan informasi utas
-p: Tentukan pid proses
Contoh
Lihat thread yang terkait dengan proses 12345
sudo ps -T -p 12345
Lihat thread yang terkait dengan semua proses
sudo ps -T
Dalam mode multi-proses, setiap proses bertanggung jawab atas tugas yang berbeda tanpa mengganggu satu sama lain.
Dalam mode multi-threading, terdapat beberapa thread dalam suatu proses, berbagi ruang memori yang sama, dan thread dapat berkomunikasi secara langsung.
pthread_create() digunakan untuk membuat thread. Setelah pembuatan berhasil, thread mulai berjalan.
Setelah pthread_create() berhasil dipanggil, ia akan mengembalikan 0, jika tidak maka akan mengembalikan kode kesalahan.
File tajuk fungsi:
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
Deskripsi Parameter:
nilai kembalian:
Melihat:
// todo : 创建一个线程,并在线程中打印出“Hello, World!”
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
// 线程函数
//@param arg 线程函数参数
void * print_hello(void *arg) {
printf("%sn",(char *)arg);
}
int main() {
pthread_t tid; //? typedef unsigned long int pthread_t;
// 创建线程
//@param tid 线程ID
//@param attr 线程属性
//@param start_routine 线程函数
//@param arg 线程函数参数
int ret = pthread_create(&tid, NULL,print_hello, "Hello, World!");
if (ret!= 0){
printf("pthread_create error!n");
return 1;
}
sleep(1); // 等待线程执行完毕
return 0;
}
pthread_exit() digunakan untuk keluar dari thread. Setelah thread selesai dieksekusi, pthread_exit() akan otomatis dipanggil untuk keluar.
File tajuk fungsi:
#include <pthread.h>
void pthread_exit(void *retval);
Deskripsi Parameter:
pthread_join() digunakan untuk menunggu thread berakhir,
Setelah memanggil pthread_join(), thread saat ini akan diblokir hingga thread berakhir.
File tajuk fungsi:
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
Deskripsi Parameter:
nilai kembalian:
// todo : 创建一个线程,并在线程中打印出“Hello, World!”
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
// 线程函数
//@param arg 线程函数参数
void * print_hello(void *arg) {
sleep(1); // 休眠1秒
printf("%sn",(char *)arg);
pthread_exit(NULL); // 线程退出
}
int main() {
pthread_t tid; //? typedef unsigned long int pthread_t;
// 创建线程
//* @param tid 线程ID
//* @param attr 线程属性
//* @param start_routine 线程函数
//* @param arg 线程函数参数
int ret = pthread_create(&tid, NULL,print_hello, "Hello, World!");
if (ret!= 0){
printf("pthread_create error!n");
return 1;
}
printf("等待线程结束...n");
// 等待线程结束
//* @param thread 线程ID
//* @param status 线程退出状态
pthread_join(tid, NULL);
return 0;
}
等待线程结束...
Hello, World!
Utas dibagi menjadi dapat digabungkan dan dilepas
// todo : 创建一个线程,并在线程中打印出“Hello, World!”
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
// 线程函数
//@param arg 线程函数参数
void * print_hello(void *arg) {
sleep(1); // 休眠1秒
printf("%sn",(char *)arg);
pthread_exit(NULL); // 线程退出
}
int main() {
pthread_t tid; //? typedef unsigned long int pthread_t;
// 创建线程
//* @param tid 线程ID
//* @param attr 线程属性
//* @param start_routine 线程函数
//* @param arg 线程函数参数
int ret = pthread_create(&tid, NULL,print_hello, "Hello, World!");
if (ret!= 0){
printf("pthread_create error!n");
return 1;
}
printf("等待线程结束...n");
// 等待线程结束
//* @param thread 线程ID
//* @param status 线程退出状态
//pthread_join(tid, NULL);//! 阻塞等待线程结束,直到线程结束后才继续往下执行
//线程分离
pthread_detach(tid); //! 分离线程,不用等待线程结束后才退出程序,该线程的资源在它终止时由系统来释放。
printf("主线程结束n");
return 0;
}
// todo : 创建多个线程,执行不同的任务
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
// 线程函数
//@param arg 线程函数参数
void * print_hello_A(void *arg) {
sleep(1); // 休眠1秒
printf("%sn",(char *)arg);
pthread_exit(NULL); // 线程退出
}
// 线程函数
//@param arg 线程函数参数
void * print_hello_B(void *arg) {
sleep(2); // 休眠2秒
printf("%sn",(char *)arg);
pthread_exit(NULL); // 线程退出
}
int main() {
pthread_t tidA; //? 存储线程ID typedef unsigned long int pthread_t;
pthread_t tidB;
// 创建线程
//* @param tid 线程ID
//* @param attr 线程属性
//* @param start_routine 线程函数
//* @param arg 线程函数参数
int retA = pthread_create(&tidA, NULL,print_hello_A, "A_ Hello, World!");
if (retA!= 0){
printf("pthread_create error!n");
return 1;
}
int retB = pthread_create(&tidB, NULL,print_hello_B, "B_ Hello, World!");
if (retB!= 0){
printf("pthread_create error!n");
return 1;
}
printf("等待线程结束...n");
// 等待线程结束
//* @param thread 线程ID
//* @param status 线程退出状态
pthread_join(tidA, NULL);//! 阻塞等待线程结束,直到线程结束后才继续往下执行
pthread_join(tidB, NULL);
printf("主线程结束n");
return 0;
}
// todo : 创建多个线程,执行相同任务
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
//? 两个线程执行相同任务,对函数中的值修改了,会不会影响到其他线程的执行?
//! 在多线程编程中,如果多个线程执行相同的任务并且对共享资源进行修改,可能会影响到其他线程的执行。
//! 这是因为多个线程共享相同的内存空间,对共享资源的修改可能会导致竞态条件(race condition),
//! 从而导致不可预测的行为。
//! print_hello函数中的变量i是局部变量,每个线程都会有自己的i副本,因此对i的修改不会影响到其他线程。
//! 但是,如果涉及到共享资源(例如全局变量或静态变量),就需要考虑线程同步的问题,以避免竞态条件。
//*局部变量:每个线程都有自己的栈空间,因此局部变量是线程私有的,不会影响到其他线程。
//*共享资源:如果多个线程访问和修改同一个全局变量或静态变量,就需要使用同步机制(如互斥锁、信号量等)来确保线程安全。
//Linux:在Linux系统中,默认的线程栈大小通常是8MB。可以使用ulimit -s命令查看和修改当前用户的线程栈大小。例如,ulimit -s 1024将线程栈大小设置为1MB。
//Windows:在Windows系统中,默认的线程栈大小是1MB。可以通过编译器选项或在创建线程时指定栈大小来修改。
// 线程函数
//@param arg 线程函数参数
void * print_hello(void *arg) {
for (char i = 'a'; i < 'z' ; ++i) {
printf("%cn", i);
sleep(1); // 休眠1秒
}
pthread_exit(NULL); // 线程退出
}
int main() {
pthread_t tid[2]={0}; //? 存储线程ID的数组 typedef unsigned long int pthread_t;
for (int i = 0; i < 2; ++i) {
// 创建线程
//* @param tid 线程ID
//* @param attr 线程属性
//* @param start_routine 线程函数
//* @param arg 线程函数参数
int retA = pthread_create(&tid[i], NULL,print_hello, NULL);
if (retA!= 0){
printf("pthread_create error!n");
return 1;
}
}
printf("等待线程结束...n");
// 等待线程结束
//* @param thread 线程ID
//* @param status 线程退出状态
pthread_join(tid[0], NULL);//! 阻塞等待线程结束,直到线程结束后才继续往下执行
pthread_join(tid[1], NULL);
printf("主线程结束n");
return 0;
}
Komunikasi antar proses lainnya juga berlaku untuk komunikasi antar thread.
Saat membuat thread anak melalui fungsi pthread_create(), parameter keempat pthread_create() adalah parameter yang diteruskan ke fungsi thread anak.
Saat keluar dari thread anak melalui fungsi pthread_exit(), Anda dapat meneruskan parameter ke thread utama.
void pth_exit(void *retval);
Saat menunggu akhir sub-utas melalui fungsi pthread_join(), dapatkan parameter kembalian sub-utas.
int pthread_join (pthread_t __th, void **__thread_return);
//二级指针获取到了pthread_exit()函数参数指针的指向地址,通过该地址可以获取到子线程的返回参数。
// todo : 线程直接通讯,子线程向父线程传参
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
// 线程函数
//@param arg 线程函数参数
void * print_hello(void *arg) {
printf("子线程开始,结束之时传递参数100的地址n");
sleep(1); // 休眠1秒
//! int num=100;//局部变量,函数结束释放内存
static int num=100;//* 静态局部变量,函数结束不释放内存,延长生命周期
pthread_exit(&num); // 线程退出
}
int main() {
pthread_t tid; //? 存储线程ID typedef unsigned long int pthread_t;
// 创建线程
//* @param tid 线程ID
//* @param attr 线程属性
//* @param start_routine 线程函数
//* @param arg 线程函数参数
int retA = pthread_create(&tid, NULL,print_hello, NULL);
if (retA!= 0){
printf("pthread_create error!n");
return 1;
}
printf("等待线程结束...n");
void* num;//获取子进程传递的参数,num指向了子进程传递的参数
// 等待线程结束
//* @param thread 线程ID
//* @param status 线程退出状态
pthread_join(tid, (void **)&num);//! 阻塞等待线程结束,直到线程结束后才继续往下执行
printf("子线程结束,传递的参数为%dn",*(int*)num);
printf("主线程结束n");
return 0;
}
Mutex adalah mekanisme sinkronisasi yang digunakan untuk mengontrol akses ke sumber daya bersama.
Keuntungan utama thread adalah kemampuannya untuk berbagi informasi melalui variabel global, namun kemudahan berbagi ini memiliki konsekuensi:
Harus dipastikan bahwa beberapa thread tidak mengubah variabel yang sama secara bersamaan
Thread tertentu tidak akan membaca variabel yang dimodifikasi oleh thread lain. Faktanya, dua thread tidak dapat mengakses bagian kritis secara bersamaan.
Prinsip dari kunci mutex adalah ketika sebuah thread mencoba memasuki area mutex, jika area mutex tersebut sudah ditempati oleh thread lain, maka thread tersebut akan diblokir hingga area mutex tersebut terlepas.
Ini pada dasarnya adalah variabel bertipe pthread_mutex_t, yang berisi nilai integer untuk mewakili status area mutex.
Jika nilainya 1, berarti sumber daya kritis saat ini dapat bersaing untuk mendapatkan akses, dan thread yang memperoleh kunci mutex dapat memasuki bagian kritis. Saat ini nilainya 0 dan thread lainnya hanya bisa menunggu.
Jika nilainya 0 berarti sumber daya kritis saat ini ditempati oleh thread lain dan tidak dapat masuk ke bagian kritis dan hanya bisa menunggu.
typedef union
{
struct __pthread_mutex_s __data; // 互斥锁的结构体
char __size[__SIZEOF_PTHREAD_MUTEX_T];// 互斥锁的大小
long int __align;// 互斥锁的对齐
} pthread_mutex_t;
Ada dua cara utama untuk menginisialisasi kunci mutex thread:
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER
Inisialisasi dinamis Inisialisasi dinamis terutama melibatkan dua fungsi: fungsi pthread_mutex_init dan fungsi pthread_mutex_destroy
Digunakan untuk menginisialisasi kunci mutex, ia menerima dua parameter: alamat kunci mutex dan atribut kunci mutex.
File tajuk fungsi:
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
Deskripsi Parameter:
nilai kembalian:
Digunakan untuk menghancurkan kunci mutex, ia menerima satu parameter: alamat kunci mutex.
File tajuk fungsi:
#include <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t *mutex);
Deskripsi Parameter:
nilai kembalian:
Contoh:
// todo : 互斥锁;创建两个线程,分别对全局变量进⾏ +1 操作
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
static int global = 0;// 全局变量
//静态初始化互斥锁
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;// 互斥锁
//动态初始化互斥锁
pthread_mutex_t mut;// 互斥锁
// 线程函数
//@param arg 线程函数参数
void * print_hello(void *arg) {
printf("子线程开始n");
int loops = *(int *)arg;
int i,tmp = 0;
for (i = 0;i < loops;i++){
pthread_mutex_lock(&mut);// 加锁
printf("子线程%d,global=%dn",i,global);
tmp = global;
tmp++;
global = tmp;
pthread_mutex_unlock(&mut);// 解锁
}
printf("子线程结束n");
pthread_exit(NULL); // 线程退出
}
int main() {
// 动态初始化互斥锁
//* @param mutex 互斥锁
//* @param attr 互斥锁属性 NULL 是默认属性
int r= pthread_mutex_init(&mut,NULL);
if (r!= 0){
printf("pthread_mutex_init error!n");
return 1;
}
pthread_t tid[2]={0}; //? 存储线程ID typedef unsigned long int pthread_t;
int arg=20;
for (int i = 0; i < 2; i++){
// 创建线程
//* @param tid 线程ID
//* @param attr 线程属性
//* @param start_routine 线程函数
//* @param arg 线程函数参数
int retA = pthread_create(&tid[i], NULL,print_hello, &arg);
if (retA!= 0){
printf("pthread_create error!n");
return 1;
}
}
printf("等待线程结束...n");
// 等待线程结束
//* @param thread 线程ID
//* @param status 线程退出状态
pthread_join(tid[0],NULL );//! 阻塞等待线程结束,直到线程结束后才继续往下执行
pthread_join(tid[1],NULL );
printf("%dn",global);
printf("主线程结束n");
// 销毁动态创建的互斥锁
//* @param mutex 互斥锁
pthread_mutex_destroy(&mut);// 销毁互斥锁
return 0;
}
Sinkronisasi thread: mengacu pada akses teratur terhadap sumber daya oleh pengunjung melalui mekanisme lain berdasarkan saling pengecualian (dalam banyak kasus).
Variabel kondisi: Mekanisme yang disediakan oleh pustaka thread khusus untuk sinkronisasi thread.
Skenario aplikasi tipikal untuk sinkronisasi thread adalah antara produsen dan konsumen.
Dalam model ini, dibagi menjadi thread produsen dan thread konsumen, dan proses sinkronisasi beberapa thread disimulasikan melalui thread ini.
Dalam model ini, diperlukan komponen-komponen berikut:
- Gudang: digunakan untuk menyimpan produk, umumnya sebagai sumber daya bersama
- Benang produsen: digunakan untuk menghasilkan produk
- Benang konsumen: digunakan untuk mengkonsumsi produk
prinsip:
Ketika tidak ada produk di gudang, konsumen harus menunggu sampai ada produk sebelum dapat mengkonsumsinya.
Ketika gudang sudah penuh dengan produk, maka thread produsen harus menunggu hingga thread konsumen mengkonsumsi produk tersebut.
Pelaku utamanya adalah konsumen
n sub-thread sebagai produser
// todo : 基于互斥锁实现⽣产者与消费者模型主线程为消费者,n 个⼦线程作为⽣产者
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
static int n = 0; // 产品数量
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;// 互斥锁
//生产者执行函数
void * dofunc(void *arg) {
int arg1 = *(int*)arg;
for (int i = 0; i <arg1; i++) {
//获取互斥锁
pthread_mutex_lock(&mutex);
//生产产品
printf("生产者%ld生产了%d个产品n",pthread_self(),++n);//! pthread_self()返回当前线程ID
//释放互斥锁
pthread_mutex_unlock(&mutex);
//休眠1秒
sleep(1);
}
pthread_exit(NULL);
}
int main() {
pthread_t tid[4]={0}; //? 存储线程ID typedef unsigned long int pthread_t;
int arr[4]={1,2,3,4};
for (int i = 0; i < 4; i++) {
// 创建线程
//* @param tid 线程ID
//* @param attr 线程属性
//* @param start_routine 线程函数
//* @param arg 线程函数参数
int retA = pthread_create(&tid[i], NULL,dofunc,&arr[i] );
if (retA!= 0){
printf("pthread_create error!n");
return 1;
}
}
//消费者执行
for (int i = 0;i<10;i++) {
//获取互斥锁
pthread_mutex_lock(&mutex);
while (n > 0){
//消费产品
printf("消费者%ld消费了1个产品:%dn",pthread_self(),n--);
}
//释放互斥锁
pthread_mutex_unlock(&mutex);
//休眠1秒
sleep(1);
}
printf("等待线程结束...n");
// 等待线程结束
//* @param thread 线程ID
//* @param status 线程退出状态
pthread_join(tid[0],NULL );//! 阻塞等待线程结束,直到线程结束后才继续往下执行
pthread_join(tid[1],NULL );
pthread_join(tid[2],NULL );
pthread_join(tid[3],NULL );
return 0;
}
Variabel kondisi adalah mekanisme sinkronisasi yang memungkinkan thread menunggu hingga kondisi tertentu dipenuhi sebelum melanjutkan berjalan.
Prinsip dari variabel kondisi adalah berisi kunci mutex dan antrian tunggu.
Kunci mutex digunakan untuk melindungi antrian tunggu dan variabel kondisi.
Sifat variabel kondisi adalah tipe pthread_cond_t
其他线程可以阻塞在这个条件变量上, 或者唤
醒阻塞在这个条件变量上的线程
typedef union
{
struct __pthread_cond_s __data;
char __size[__SIZEOF_PTHREAD_COND_T];
__extension__ long long int __align;
} pthread_cond_t;
Inisialisasi variabel kondisi dibagi menjadi inisialisasi statis dan inisialisasi dinamis.
Untuk variabel kondisi yang diinisialisasi secara statis, Anda harus terlebih dahulu mendefinisikan variabel bertipe pthread_cond_t, lalu menginisialisasinya ke PTHREAD_COND_INITIALIZER.
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
Untuk menginisialisasi variabel kondisi secara dinamis, Anda perlu mendefinisikan variabel bertipe pthread_cond_t terlebih dahulu, lalu memanggil fungsi pthread_cond_init untuk menginisialisasinya.
File tajuk fungsi:
#include <pthread.h>
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
Deskripsi Parameter:
nilai kembalian:
Digunakan untuk menghancurkan variabel kondisi, ia menerima satu parameter: alamat variabel kondisi.
File tajuk fungsi:
#include <pthread.h>
int pthread_cond_destroy(pthread_cond_t *cond);
Deskripsi Parameter:
nilai kembalian:
Penggunaan variabel kondisi dibagi menjadi menunggu dan pemberitahuan
Fungsi menunggu pthread_cond_wait() menerima tiga parameter: alamat variabel kondisi, alamat kunci mutex, dan waktu tunggu.
File tajuk fungsi:
#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);
Deskripsi Parameter:
nilai kembalian:
fungsi notifikasi
pthread_cond_signal() menerima satu parameter: alamat variabel kondisi.
File tajuk fungsi:
#include <pthread.h>
int pthread_cond_signal(pthread_cond_t *cond);
Deskripsi Parameter:
nilai kembalian:
Beritahu semua fungsi
pthread_cond_broadcast() menerima satu parameter: alamat variabel kondisi.
File tajuk fungsi:
#include <pthread.h>
int pthread_cond_broadcast(pthread_cond_t *cond);
Deskripsi Parameter:
nilai kembalian:
step 1 : 消费者线程判断消费条件是否满足 (仓库是否有产品),如果有产品可以消费,则可以正
常消费产品,然后解锁
step 2 : 当条件不能满足时 (仓库产品数量为 0),则调用 pthread_cond_wait 函数, 这个函数
具体做的事情如下:
在线程睡眠之前,对互斥锁解锁
让线程进⼊到睡眠状态
等待条件变量收到信号时 唤醒,该函数重新竞争锁,并获取锁后,函数返回
step 3 :重新判断条件是否满足, 如果不满足,则继续调用 pthread_cond_wait 函数
step 4 : 唤醒后,从 pthread_cond_wait 返回,消费条件满足,则正常消费产品
step 5 : 释放锁,整个过程结束
Mengapa variabel kondisi perlu digunakan bersama dengan kunci mutex?
Lindungi data bersama:
Kunci mutex digunakan untuk melindungi data bersama dan memastikan bahwa hanya satu thread yang dapat mengakses dan mengubah data pada saat yang bersamaan.
Hal ini untuk menghindari perlombaan dan inkonsistensi data.
Variabel kondisi digunakan untuk komunikasi antar thread untuk memberi tahu thread lain bahwa kondisi tertentu telah terpenuhi.
Namun, pengoperasian variabel kondisi dengan sendirinya tidak memberikan perlindungan bagi data bersama, sehingga perlu digunakan bersama dengan kunci mutex.
Hindari bangun yang salah:
Salah satu karakteristik dari variabel kondisi adalah bahwa kebangkitan palsu dapat terjadi.
Artinya, thread dibangunkan tanpa pemberitahuan eksplisit. Untuk menghindari operasi yang salah yang disebabkan oleh situasi ini,
Thread perlu dicek ulang apakah kondisinya benar-benar terpenuhi setelah bangun tidur.
Menggunakan mutex memastikan bahwa data yang dibagikan tidak diubah oleh thread lain saat memeriksa kondisi, sehingga menghindari kesalahan yang disebabkan oleh bangun palsu.
Pastikan notifikasinya benar:
Ketika sebuah thread memberi tahu thread lain melalui variabel kondisi, thread tersebut perlu memastikan bahwa data yang dibagikan telah diperbarui sebelum memberi tahu.
Mutex menjamin hal ini, memastikan bahwa semua operasi pembaruan data telah selesai sebelum melepaskan kunci.
Demikian pula, thread yang menerima notifikasi perlu menahan mutex sebelum memeriksa kondisi untuk memastikan bahwa data stabil ketika kondisi diperiksa.
Menerapkan pola sinkronisasi yang kompleks:
Menggabungkan kunci mutex dengan variabel kondisi dapat menerapkan pola sinkronisasi yang lebih kompleks, seperti masalah produsen-konsumen, masalah pembaca-penulis, dll. Mutex melindungi data bersama, dan variabel kondisi digunakan untuk koordinasi dan komunikasi antar thread.
// todo : 条件变量
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdlib.h>
static int number = 0;// 产品数量
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;// 互斥锁
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;// 条件变量
// 线程函数
//@param arg 线程函数参数
void * thread_handler(void *arg) {
int cnt = atoi((char *)arg);// 获取线程参数
int i,tmp;// 临时变量
for(i = 0;i < cnt;i++){// 生产产品
pthread_mutex_lock(&mtx);// 上锁
printf("线程 [%ld] ⽣产⼀个产品,产品数量为:%dn",pthread_self(),++number);
pthread_mutex_unlock(&mtx);// 解锁
//! 唤醒cond阻塞的线程
//! @param cond 条件变量
//pthread_cond_signal(&cond);//! 只能唤醒一个线程,如果有多个线程在等待,则只有一个线程会被唤醒
//唤醒所有线程
pthread_cond_broadcast(&cond);
}
pthread_exit((void *)0);// 线程退出
}
int main(int argc,char *argv[]) {
pthread_t tid[argc-1];// 线程ID
int i;
int err;
int total_of_produce = 0;// 总共生产的产品数量
int total_of_consume = 0;// 总共消费的产品数量
bool done = false;// 是否完成生产
//循环创建线程
for (i = 1;i < argc;i++){
total_of_produce += atoi(argv[i]);// 计算总共需要生产的产品数量
// 创建线程
err = pthread_create(&tid[i-1],NULL,thread_handler,(void *)argv[i]);
if (err != 0){
perror("[ERROR] pthread_create(): ");
exit(EXIT_FAILURE);
}
}
//消费者
for (;;){
//*先获取锁,再进行条件变量的等待
pthread_mutex_lock(&mtx);// 上锁
//*while循环来判断条件,避免虚假唤醒
while(number == 0) {// 等待生产者生产产品
//! 等待条件变量
//! @param cond 条件变量
//! @param mtx 互斥锁
//! 函数中会释放互斥锁,并阻塞线程,
//! 直到条件变量被唤醒,再重新竞争互斥锁,获取互斥锁并继续执行
pthread_cond_wait(&cond, &mtx);
}
while(number > 0){
total_of_consume++;// 总共消费的产品数量
printf("消费⼀个产品,产品数量为:%dn",--number);// 消费产品
done = total_of_consume >= total_of_produce;// 是否完成生产
}
pthread_mutex_unlock(&mtx);// 解锁
if (done)// 是否完成生产
break;
}
// 等待线程退出
for(i = 0;i < argc-1;i++){
pthread_join(tid[i],NULL);
}
return 0;
}
//循环创建线程
for (i = 1;i < argc;i++){
total_of_produce += atoi(argv[i]);// 计算总共需要生产的产品数量
// 创建线程
err = pthread_create(&tid[i-1],NULL,thread_handler,(void *)argv[i]);
if (err != 0){
perror("[ERROR] pthread_create(): ");
exit(EXIT_FAILURE);
}
}
//消费者
for (;;){
//*先获取锁,再进行条件变量的等待
pthread_mutex_lock(&mtx);// 上锁
//*while循环来判断条件,避免虚假唤醒
while(number == 0) {// 等待生产者生产产品
//! 等待条件变量
//! @param cond 条件变量
//! @param mtx 互斥锁
//! 函数中会释放互斥锁,并阻塞线程,
//! 直到条件变量被唤醒,再重新竞争互斥锁,获取互斥锁并继续执行
pthread_cond_wait(&cond, &mtx);
}
while(number > 0){
total_of_consume++;// 总共消费的产品数量
printf("消费⼀个产品,产品数量为:%dn",--number);// 消费产品
done = total_of_consume >= total_of_produce;// 是否完成生产
}
pthread_mutex_unlock(&mtx);// 解锁
if (done)// 是否完成生产
break;
}
// 等待线程退出
for(i = 0;i < argc-1;i++){
pthread_join(tid[i],NULL);
}
return 0;
}