Kaedah penyegerakan termasuk: 1. Kunci Mutex ialah pembolehubah global khas dengan dua keadaan: kunci dan buka kunci Kunci mutex boleh diperolehi oleh benang tertentu Apabila kunci mutex dipegang oleh benang tertentu , mutex akan dikunci dan menjadi keadaan kunci Selepas itu, hanya benang yang mempunyai kuasa untuk membuka kunci 2. Kunci putaran adalah gelung tak terhingga 3. Semafor digunakan untuk mengawal bilangan utas yang mengakses sumber kongsi terhad; 4. Pembolehubah keadaan 5. Kunci baca-tulis;
Persekitaran pengendalian tutorial ini: sistem linux7.3, komputer Dell G3.
Penyegerakan benang bermaksud bahawa apabila utas beroperasi pada sumber kritikal, tiada utas lain boleh beroperasi pada sumber ini Sehingga utas menyelesaikan operasi, utas lain boleh beroperasi, iaitu, rentak yang diselaraskan, membenarkan utas untuk. dijalankan mengikut susunan yang telah ditetapkan. Terdapat 6 kaedah penyegerakan benang: kunci mutex, kunci putaran, semaphore, pembolehubah keadaan, kunci baca-tulis dan halangan.
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 berjalan adalah seperti berikut:
# gcc no_lock_demo.c -o no_lock_demo.out -pthread # ./no_lock_demo.out ticket_num=-2
Hasil akhir tidak tetap dan mungkin 0 atau -1 penyegerakan benang perlu diperkenalkan untuk memastikan 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 boleh dikawal oleh benang A tertentu memperolehnya Apabila mutex dipegang oleh benang, mutex akan dikunci dan bertukar menjadi keadaan kunci Selepas itu, hanya benang itu yang mempunyai kuasa untuk membuka kunci, dan benang lain yang ingin diperolehi 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; }
Seperti namanya, kunci putaran ialah gelung tak terhingga, mengundi secara berterusan Apabila benang tidak memperoleh kunci putaran, ia tidak akan memasuki keadaan tidur menyekat seperti kunci mutex, tetapi akan mengundi secara berterusan untuk memperoleh kunci Jika kunci putaran boleh dilepaskan dengan cepat, prestasi akan menjadi sangat tinggi Jika kunci putaran tidak dapat dilepaskan untuk jangka masa yang lama, atau terdapat sejumlah besar penyekatan IO, ia akan menyebabkan benang lain yang memperoleh kunci untuk mengundi. secara berterusan, mengakibatkan penggunaan CPU mencecah 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 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 membolehkan memanggil The benang berjalan apabila syarat tertentu dipenuhi Apabila syarat tidak dipenuhi, benang terhalang dan menunggu untuk dibangkitkan. 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; }
读写锁可以有三种状态:读模式下加锁状态,写模式下加锁状态,不加锁状态。一次只有一个线程可以占有写模式的读写锁,但是多个线程可以同时占有读模式的读写锁。读写锁也叫做共享-独占锁,当读写锁以读模式锁住时,它是以共享模式锁住的,当它以写模式锁住时,它是以独占模式锁住的,读读共享,读写互斥。
相关方法:
// 创建读写锁 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); // 尝试加写锁,非阻塞
例子:
#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 Apakah beberapa kaedah penyegerakan berbilang benang dalam Linux?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!