Rumah > Artikel > Operasi dan penyelenggaraan > Terdapat beberapa cara untuk melaksanakan penyegerakan benang dalam Linux
6 cara: 1. Kunci Mutex, yang pada asasnya merupakan pembolehubah global khas dengan dua keadaan kunci dan buka kunci 2. Kunci putaran, yang merupakan gelung tak terhingga dan terus mengundi 3 , Semaphores, digunakan untuk mengawal; bilangan utas yang mengakses sumber yang dikongsi terhad; hanya boleh dikejutkan pada satu masa. Terdapat satu utas yang boleh menduduki kunci baca-tulis dalam mod tulis 6. Halangan ialah mekanisme penyegerakan untuk pengguna menyelaraskan kerja selari berbilang utas.
Persekitaran pengendalian tutorial ini: sistem linux7.3, komputer Dell G3.
Berikut ialah contoh tidak selamat benang:
#include<stdio.h> #include<pthread.h> int ticket_num=10000000; void *sell_ticket(void *arg) { while(ticket_num>0) { ticket_num--; } } int main() { pthread_t t1,t2,t3; pthread_create(&t1, NULL, &sell_ticket, NULL); pthread_create(&t2, NULL, &sell_ticket, NULL); pthread_create(&t3, NULL, &sell_ticket, NULL); pthread_join(t1, NULL); pthread_join(t2, NULL); pthread_join(t3, NULL); printf("ticket_num=%d\n", ticket_num); return 0; }
Hasil yang dijalankan adalah seperti berikut :
# gcc no_lock_demo.c -o no_lock_demo.out -pthread # ./no_lock_demo.out ticket_num=-2
Hasil akhir operasi tidak tetap dan mungkin 0 atau -1 Jika pembolehubah nombor_tiket mewakili inventori, maka inventori akan menjadi negatif, jadi penyegerakan benang perlu diperkenalkan. Pastikan keselamatan benang.
Linux menyediakan pelbagai cara untuk mengendalikan penyegerakan benang, yang paling biasa digunakan ialah kunci mutex, kunci putaran dan semaphore.
Intipati kunci mutex ialah pembolehubah global khas, yang mempunyai dua keadaan: kunci dan buka kunci Kunci mutex yang tidak berkunci boleh dikawal oleh a benang Diperolehi, apabila mutex dipegang oleh benang, mutex akan dikunci dan berubah menjadi keadaan kunci Selepas itu, hanya benang yang mempunyai kuasa untuk membuka kunci, dan benang lain yang ingin mendapatkan mutex akan disekat. sehingga Mutex dibuka.
Jenis kunci mutex:
Kunci biasa (PTHREAD_MUTEX_NORMAL): Jenis lalai kunci mutex. Apabila benang mengunci kunci biasa, benang selebihnya yang meminta kunci akan membentuk baris gilir menunggu dan mendapatkan kunci mengikut keutamaan selepas ia dibuka kunci Jenis kunci ini memastikan keadilan dalam peruntukan sumber. Jika benang mengunci kunci biasa yang telah dikunci semula, ia akan menyebabkan kebuntuan membuka kunci biasa yang telah dikunci oleh benang lain, atau membuka kunci biasa yang telah dibuka semula, akan membawa kepada akibat yang tidak dapat diramalkan.
Kunci semakan ralat (PTHREAD_MUTEX_ERRORCHECK): Jika benang mengunci kunci semakan ralat yang sudah dikunci semula, operasi mengunci mengembalikan EDEADLK untuk utas yang telah dikunci oleh utas lain Jika kunci pengesanan ralat dibuka kunci atau kunci pengesanan ralat yang sudah dibuka dibuka semula, operasi buka kunci kembali kepada EPERM.
Kunci bersarang (PTHREAD_MUTEX_RECURSIVE): Kunci ini membenarkan benang menguncinya beberapa kali sebelum melepaskan kunci tanpa kebuntuan jika utas lain ingin mendapatkan kunci ini, kunci semasa Pemilik mesti melakukan berbilang operasi buka kunci; untuk membuka kunci bersarang yang telah dikunci oleh benang lain, atau untuk membuka kunci bersarang lagi, operasi membuka kunci mengembalikan EPERM.
Kunci lalai (PTHREAD_MUTEX_DEFAULT): Jika benang mengunci kunci lalai yang sudah terkunci semula, atau membuka kunci lalai yang telah dikunci oleh benang lain, atau Membuka kunci kunci lalai yang tidak berkunci akan membawa kepada akibat yang tidak dapat diramalkan; kunci ini mungkin dipetakan kepada salah satu daripada tiga kunci di atas apabila dilaksanakan.
Kaedah berkaitan:
// 静态方式创建互斥锁 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 动态方式创建互斥锁,其中参数mutexattr用于指定互斥锁的类型,具体类型见上面四种,如果为NULL,就是普通锁。 int pthread_mutex_init (pthread_mutex_t* mutex,const pthread_mutexattr_t* mutexattr); int pthread_mutex_lock(pthread_mutex_t *mutex); // 加锁,阻塞 int pthread_mutex_trylock(pthread_mutex_t *mutex); // 尝试加锁,非阻塞 int pthread_mutex_unlock(pthread_mutex_t *mutex); // 解锁
Contoh:
#include<stdio.h> #include<pthread.h> int ticket_num=10000000; pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER; void *sell_ticket(void *arg) { while(ticket_num>0) { pthread_mutex_lock(&mutex); if(ticket_num>0) { ticket_num--; } pthread_mutex_unlock(&mutex); } } int main() { pthread_t t1,t2,t3; pthread_create(&t1, NULL, &sell_ticket, NULL); pthread_create(&t2, NULL, &sell_ticket, NULL); pthread_create(&t3, NULL, &sell_ticket, NULL); pthread_join(t1, NULL); pthread_join(t2, NULL); pthread_join(t3, NULL); printf("ticket_num=%d\n", ticket_num); return 0; }
Sebagai namanya mencadangkan, kunci putaran ialah gelung tak terhingga yang terus mengundi Apabila benang tidak memperoleh kunci putaran, ia tidak akan memasuki keadaan tidur menyekat seperti kunci mutex, tetapi akan terus mengundi untuk memperoleh kunci berputar Jika kunci boleh dilepaskan dengan cepat, prestasi akan menjadi sangat tinggi Jika kunci putaran tidak dapat dilepaskan untuk masa yang lama, atau terdapat banyak penyekatan IO, ia akan menyebabkan benang lain yang memperoleh kunci untuk mengundi secara berterusan. , menyebabkan Kadar penggunaan CPU mencapai 100%, terutamanya masa CPU.
Kaedah berkaitan:
int pthread_spin_init(pthread_spinlock_t *lock, int pshared); // 创建自旋锁 int pthread_spin_lock(pthread_spinlock_t *lock); // 加锁,阻塞 int pthread_spin_trylock(pthread_spinlock_t *lock); // 尝试加锁,非阻塞 int pthread_spin_unlock(pthread_spinlock_t *lock); // 解锁
Contoh:
#include<stdio.h> #include<pthread.h> int ticket_num=10000000; pthread_spinlock_t spinlock; void *sell_ticket(void *arg) { while(ticket_num>0) { pthread_spin_lock(&spinlock); if(ticket_num>0) { ticket_num--; } pthread_spin_unlock(&spinlock); } } int main() { pthread_spin_init(&spinlock, 0); pthread_t t1,t2,t3; pthread_create(&t1, NULL, &sell_ticket, NULL); pthread_create(&t2, NULL, &sell_ticket, NULL); pthread_create(&t3, NULL, &sell_ticket, NULL); pthread_join(t1, NULL); pthread_join(t2, NULL); pthread_join(t3, NULL); printf("ticket_num=%d\n", ticket_num); return 0; }
Semaphore ialah pembilang, digunakan Digunakan untuk mengawal bilangan utas yang mengakses sumber kongsi terhad.
Kaedah berkaitan:
// 创建信号量 // pshared:一般取0,表示调用进程的信号量。非0表示该信号量可以共享内存的方式,为多个进程所共享(Linux暂不支持)。 // value:信号量的初始值,可以并发访问的线程数。 int sem_init (sem_t* sem, int pshared, unsigned int value); int sem_wait (sem_t* sem); // 信号量减1,信号量为0时就会阻塞 int sem_trywait (sem_t* sem); // 信号量减1,信号量为0时返回-1,不阻塞 int sem_timedwait (sem_t* sem, const struct timespec* abs_timeout); // 信号量减1,信号量为0时阻塞,直到abs_timeout超时返回-1 int sem_post (sem_t* sem); // 信号量加1
Contoh:
#include<stdio.h> #include<pthread.h> #include <semaphore.h> int ticket_num=10000000; sem_t sem; void *sell_ticket(void *arg) { while(ticket_num>0) { sem_wait(&sem); if(ticket_num>0) { ticket_num--; } sem_post(&sem); } } int main() { sem_init(&sem, 0, 1); // value=1表示最多1个线程同时访问共享资源,与互斥量等价 pthread_t t1,t2,t3; pthread_create(&t1, NULL, &sell_ticket, NULL); pthread_create(&t2, NULL, &sell_ticket, NULL); pthread_create(&t3, NULL, &sell_ticket, NULL); pthread_join(t1, NULL); pthread_join(t2, NULL); pthread_join(t3, NULL); printf("ticket_num=%d\n", ticket_num); return 0; }
Pembolehubah keadaan membenarkan urutan panggilan untuk Berjalan apabila syarat tertentu dipenuhi Sekat dan menunggu untuk dikejutkan apabila syarat tidak dipenuhi. Ia mesti digunakan bersama dengan kunci mutex.
Pembolehubah bersyarat sering digunakan dalam model pengeluar dan pengguna.
Kaedah berkaitan:
pthread_cond_t cond=PTHREAD_COND_INITIALIZER; // 创建条件变量,一个互斥锁可以对应多个条件变量 int pthread_cond_wait (pthread_cond_t* cond,pthread_mutex_t* mutex); // 阻塞等待条件满足,同时释放互斥锁mutex int pthread_cond_timedwait (pthread_cond_t* cond, pthread_mutex_t* mutex, const struct timespec* abstime); // 带超时的阻塞等待条件满足,同时释放互斥锁mutex // 从条件变量cond中唤出一个线程,令其重新获得原先的互斥锁 // 被唤出的线程此刻将从pthread_cond_wait函数中返回,但如果该线程无法获得原先的锁,则会继续阻塞在加锁上。 int pthread_cond_signal (pthread_cond_t* cond); // 从条件变量cond中唤出所有线程 int pthread_cond_broadcast (pthread_cond_t* cond);
Contoh:
#include<stdio.h> #include<pthread.h> int max_buffer=10; int count=0; pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER; pthread_cond_t notempty=PTHREAD_COND_INITIALIZER; pthread_cond_t notfull=PTHREAD_COND_INITIALIZER; void *produce(void *args) { while(1) { pthread_mutex_lock(&mutex); while(count == max_buffer) { printf("buffer is full, wait...\n"); pthread_cond_wait(¬full, &mutex); } printf("produce ...\n"); count++; sleep(1); pthread_cond_signal(¬empty); pthread_mutex_unlock(&mutex); } } void *consumer(void *args) { while(1) { pthread_mutex_lock(&mutex); while(count == 0) { printf("buffer is empty, wait...\n"); pthread_cond_wait(¬empty, &mutex); } printf("consumer ...\n"); count--; sleep(1); pthread_cond_signal(¬full); pthread_mutex_unlock(&mutex); } } int main() { pthread_t t1,t2,t3,t4; pthread_create(&t1, NULL, &produce, NULL); pthread_create(&t2, NULL, &produce, NULL); pthread_create(&t3, NULL, &consumer, NULL); pthread_create(&t4, NULL, &consumer, NULL); pthread_join(t1, NULL); return 0; }
Terdapat tiga jenis kunci baca-tulis Status: keadaan terkunci dalam mod baca, keadaan terkunci dalam mod tulis dan keadaan tidak berkunci. Hanya satu utas boleh memegang kunci baca-tulis dalam mod tulis pada satu masa, tetapi beberapa utas boleh memegang kunci baca-tulis dalam mod baca pada masa yang sama. Kunci baca-tulis juga dipanggil kunci kongsi-eksklusif Apabila kunci baca-tulis dikunci dalam mod baca, ia dikunci dalam mod kongsi Apabila ia dikunci dalam mod tulis, ia dikunci dalam mod eksklusif. kunci tulis Dikongsi, baca dan tulis saling eksklusif.
Hanya satu utas boleh menduduki kunci baca-tulis dalam mod tulis pada satu-satu masa, tetapi beberapa utas boleh menahan kunci baca-tulis dalam mod baca pada masa yang sama. Oleh itu kunci baca-tulis membenarkan keselarian yang lebih tinggi berbanding dengan mutex. Kunci baca-tulis sangat sesuai untuk situasi di mana bilangan bacaan kepada struktur data jauh lebih besar daripada bilangan penulisan.
Kaedah berkaitan:
// 创建读写锁 pthread_rwlock_t rwlock=PTHREAD_RWLOCK_INITIALIZER; int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); // 加读锁,阻塞 int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); // 加写锁,阻塞 int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); // 释放读锁或者写锁 int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); // 尝试加读锁,非阻塞 int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); // 尝试加写锁,非阻塞
Contoh:
#include <stdio.h> #include <pthread.h> pthread_rwlock_t rwlock=PTHREAD_RWLOCK_INITIALIZER; void *read(void *arg) { while(1) { pthread_rwlock_rdlock(&rwlock); rintf("read message.\n"); sleep(1); pthread_rwlock_unlock(&rwlock); sleep(1); } } void *write(void *arg) { while(1) { pthread_rwlock_wrlock(&rwlock); printf("write message.\n"); sleep(1); pthread_rwlock_unlock(&rwlock); sleep(1); } } int main(int argc,char *argv[]) { pthread_t t1,t2,t3; pthread_create(&t1, NULL, &read, NULL); pthread_create(&t2, NULL, &read, NULL); pthread_create(&t3, NULL, &write, NULL); pthread_join(t1, NULL); return 0; }
屏障(barrier)是用户协调多个线程并行工作的同步机制。屏障允许每个线程等待,直到所有的合作线程都到达某一点,然后所有线程都从该点继续执行。pthread_join函数就是一种屏障,允许一个线程等待,直到另一个线程退出。但屏障对象的概念更广,允许任意数量的线程等待,直到所有的线程完成处理工作,而线程不需要退出,当所有的线程达到屏障后可以接着工作。
相关方法:
// 创建屏障 int pthread_barrier_init(pthread_barrier_t *barrier,const pthread_barrrierattr_t *attr,unsigned int count) // 阻塞等待,直到所有线程都到达 int pthread_barrier_wait(pthread_barrier_t *barrier)
例子:
#include <stdio.h> #include <pthread.h> pthread_barrier_t barrier; void *go(void *arg){ sleep (rand () % 10); printf("%lu is arrived.\n", pthread_self()); pthread_barrier_wait(&barrier); printf("%lu go shopping...\n", pthread_self()); } int main() { pthread_barrier_init(&barrier, NULL, 3); pthread_t t1,t2,t3; pthread_create(&t1, NULL, &go, NULL); pthread_create(&t2, NULL, &go, NULL); pthread_create(&t3, NULL, &go, NULL); pthread_join(t1, NULL); return 0; }
相关推荐:《Linux视频教程》
Atas ialah kandungan terperinci Terdapat beberapa cara untuk melaksanakan penyegerakan benang dalam Linux. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!