任何计算机系统中,系统资源都是有限的。在多进程或多线程程序组成的系统中,必然会存在对资源的竞争关系。按照资源需求的不同,系统中同步类型大致可分为三种:
- 数据:它允许并发线程/进程安全的访问一个内存块。
- 硬件:当多个进程或线程需要一个或多个硬件支持时,它允许并发进程或线程安全的访问硬件,并且对于实时性和任务的优先级有一定的要求。
- 任务:它强制执行合理过程的前置条件和后置条件。
为了控制控制竞争条件及并发线程或进程安全的访问某个内存块,我们需要对数据的访问进行同步。保证对内存的使用和更改都是安全的。在多线程环境中,任务代码试图访问和其他并发的线程或进程共享的内存,全局变量,或文件时,需要数据同步,这被称作临界区。临界区是可以安全读/写,关闭文件,读写全局变量或数据结构的代码块。
1,临界区
临界区是访问共享资源的代码块,它通过一个入口点和出口点来标记。
在使用的过程中有三个条件:
(1) 如果一个任务位于其临界区中,其他共享资源的任务不能在它们的临界区中执行。它们将被阻塞。这被称作互斥(mutual exclusion)。
(2) 如果没有哪个任务位于临界区中,则任何被阻塞的任务可以进入临界区运行,这被称作进行(progress)。
(3) 应当过一定时间,才允许某个任务重新进入临界区,这个时间段被称作有限等待。(bounded wait)
2,PRAM模型 (Parallel Random-Access Machine)
并行随机访问计算机,是一个简化的理论模型,其中包含被标记为P1,P2,P3,... ... ,PN 的N个处理器,这些处理器共享全局内存。所以处理器对共享全局内存进行同时的读写访问。处理器在一个不可中断的时间单元内对共享的全局内存进行访问。PRAM 模型有4种算法可被用来访问全局的共享内存:
(1) 当同时读相同的内存片段时,允许使用并行的读算法,,不会造成数据破坏。
(2) 并发写算法允许多个处理器对共享内存进行写入。
(3) 互斥读算法被用来保证不会有2个处理器同时对相同的内存位置进行读取。
(4) 互斥写算法被用来保证不会有2个处理器同时对相同的内存位置进行写入。
上述的读写算法可以被组合为不同的读写组合算法
互斥读互斥写 (EREW) 意味着对共享内存访问的串行化。
并发读并发写 (CRCW) 最为灵活,但也意味着会产生更多的并行任务的问题。
3,并发任务的执行顺序
并发任务间不光对数据的访问需要同步,任务间的执行顺序也需要进行协调。
协作任务间有4种顺序关系:
基本信号量操作 | 描 述 |
---|---|
初始化 | 分配信号量所需要的内存并赋初始值,决定信号量是私有地,共享的,被占有的还是未被占有的。 |
请求占有权 | 发出占有信号量的请求,如果信号量被其他线程占有,则当前线程阻塞。 |
释放占有权 | 释放信号量,从而使他可被阻塞的线程得到。 |
尝试占有权 | 测试信号量的占有权,如果信号量已经被占有,请求方不会阻塞,而是继续运行,在继续之前可以等待一段时间。 |
销毁 | 释放信号量相关的内存,如果信号量被占有或还有线程在等待,则该信号量不能被销毁。 |
using namespace std; #include <semaphore.h> #include <iostream> #include <fstream> #include <string> #include <fcntl.h> #include <unistd.h> int main() { string str; const char* Name; sem_t* pSem; ifstream oInFile("out_text.txt"); cout << "check file is open" << endl; if (oInFile.is_open()) { Name = "sem_test"; cout << "open sem" << endl; pSem = sem_open(Name, O_CREAT, O_RDWR, 1); sem_unlink(Name); cout << "file is eof or good" << endl; while(!oInFile.eof() && oInFile.good()) { sleep(1); // for testing with the file output progress cout << "wait sem" << endl; sem_wait(pSem); getline(oInFile, str); cout << str << endl; cout << "sem post" << endl; sem_post(pSem); } cout << "-------------------------" << endl; oInFile.close(); } return 0; }
using namespace std; #include <semaphore.h> #include <iostream> #include <fstream> #include <fcntl.h> #include <unistd.h> int main() { int nLoop, PN; sem_t *pSem; const char* strName; ofstream oOutStream("out_text.txt", ios::app); PN = 100; nLoop = 100; strName = "sem_test"; cout << "open sem" << endl; pSem = sem_open(strName, O_CREAT, O_RDWR, 1); sem_unlink(strName); cout << "writing information" << endl; for (int i = 0; i < nLoop; i++) { cout << "wait sem "<< i << endl; sleep(2); // for testing with the file input progress sem_wait(pSem); oOutStream << "Process" << PN << " counting: " << i << endl; cout << "post sem " << i << endl; sem_post(pSem); } cout << "close file" << endl; oOutStream.close(); return 0; }
互 斥 量 操 作 | 函数原型 #include<pthread.h> |
---|---|
初始化 | int pthread_mutex_init(pthread_mutex_t * mutex const pthread_mtexattr_t * attr) |
请求占有权 | int pthread_mutex_lock(pthread_mutex_t* mutex) int pthread_mutex_timedlock(pthread_mutex_t* mutex cosnt struct timespce* abs_timeout) |
释放占有权 | int pthread_mutex_unlock(pthread_mutex_t* mutex) |
尝试占有权 | int pthread_mutex_trylock(pthread_mutex_t * mutex) |
销毁 | int pthread_mutex_destroy(pthread_mutex_t * mutex) |
using namespace std; #include <pthread.h> #include <iostream> int Answer = 10; pthread_mutex_t Mutex = PTHREAD_MUTEX_INITIALIZER; void *task1(void* X) { pthread_mutex_lock(&Mutex); Answer = Answer * 32; pthread_mutex_unlock(&Mutex); cout << "Thread A Answer = " << Answer << endl; } void *task2(void* X) { pthread_mutex_lock(&Mutex); Answer = Answer / 2; pthread_mutex_unlock(&Mutex); cout << "Thread B Answer = " << Answer << endl; } void *task3(void* X) { pthread_mutex_lock(&Mutex); Answer = Answer + 5; pthread_mutex_unlock(&Mutex); cout << "Thread C Answer = " << Answer << endl; } int main() { pthread_t ThreadA, ThreadB, ThreadC; cout << "Answer = " << Answer << endl; pthread_create(&ThreadA, NULL, task1, NULL); pthread_create(&ThreadB, NULL, task2, NULL); pthread_create(&ThreadC, NULL, task3, NULL); pthread_join(ThreadA, NULL); pthread_join(ThreadB, NULL); pthread_join(ThreadC, NULL); cout << "Answer = " << Answer << endl; return 0; }
读-写锁操作 | 函数原型 #include <pthread.h> |
---|---|
初始化 | int pthread_rwlock_init(pthread_rwlock_t* rwlock, const pthread_rwlockattr_t *attr) |
请求占有权 | int pthread_rwlock_rdlock(pthread_rwlock_t * rwlock) int pthread_rwlock_wrlock(pthread_rwlock_t * rwlock) int pthread_rwlock_timedrelock(pthread_rwlock_t* rwlock, const struct timespec* abs_timeout) int pthread_rwlock_timedwrlock(pthread_rwlock_t* rwlock, const struct timespec* abs_timeout) |
释放占有权 | int pthread_rwlock_unlock(pthread_rwlock_t* rwlock) |
尝试占有权 | int pthread_rwlock_tryrdlock(pthread_rwlock_t* rwlock) int pthread_rwlock_trywrlock(pthread_rwlock_t* rwlock) |
销毁 | int pthread_rwlock_destroy(pthread_rwlock_t* rwlock) |
using namespace std; #include <pthread.h> #include <iostream> #include <unistd.h> pthread_t ThreadA, ThreadB, ThreadC, ThreadD; pthread_rwlock_t RWLock; int nCounter = 0; void *product1(void* X) { while(1) { sleep(2); // Sleep must before lock, or it will always lock pthread_rwlock_wrlock(&RWLock); if (nCounter > 100) { pthread_rwlock_unlock(&RWLock); break; } nCounter += 2; cout << "Thread A counter+2 = " << nCounter << endl; pthread_rwlock_unlock(&RWLock); } } void *product2(void* X) { while(1) { sleep(3); // Sleep must before lock, or it will always lock pthread_rwlock_wrlock(&RWLock); if (nCounter > 100) { pthread_rwlock_unlock(&RWLock); break; } nCounter++; cout << "Thread C counter++ = " << nCounter << endl; pthread_rwlock_unlock(&RWLock); } } void *consumer1(void* X) { while(1) { sleep(1); // Sleep must before lock, or it will always lock pthread_rwlock_rdlock(&RWLock); if (nCounter > 100) { pthread_rwlock_unlock(&RWLock); break; } cout << "Thread B: " << nCounter << endl; pthread_rwlock_unlock(&RWLock); } } void *consumer2(void* X) { while(1) { sleep(1); // Sleep must before lock, or it will always lock pthread_rwlock_rdlock(&RWLock); if (nCounter > 100) { pthread_rwlock_unlock(&RWLock); break; } cout << "Thread D: " << nCounter << endl; pthread_rwlock_unlock(&RWLock); } } int main() { pthread_rwlock_init(&RWLock, NULL); pthread_create(&ThreadA, NULL, product1 , NULL); pthread_create(&ThreadB, NULL, consumer1, NULL); pthread_create(&ThreadC, NULL, product2 , NULL); pthread_create(&ThreadD, NULL, consumer2, NULL); pthread_join(ThreadA, NULL); pthread_join(ThreadB, NULL); pthread_join(ThreadC, NULL); pthread_join(ThreadD, NULL); return 0; }
条件变量操作 | 函数原型 #include <pthread.h> |
---|---|
初始化 | int pthread_cond_init(pthread_cond_t * cond, const pthread_cond_attr* attr) pthread_cond_t cond = PTHREAD_COND_INITIALIZER |
发信号 | int pthread_cond_signal(pthread_cond_t* cond) int pthread_cond_broadcast(pthread_cond_t* cond) |
销毁 | int pthread_cond_destroy(pthread_cond_t * cond) |
using namespace std; #include <pthread.h> #include <iostream> #include <unistd.h> int Number; pthread_t ThreadA, ThreadB; pthread_mutex_t Mutex, EventMutex; pthread_cond_t Event; void* worker1(void* X) { for (int i = 1; i < 10; i++) { sleep(1); cout << "worker 1 locking" << endl; pthread_mutex_lock(&Mutex); Number++; pthread_mutex_unlock(&Mutex); cout << "worker 1 Number = " << Number << endl; if (Number == 7) pthread_cond_signal(&Event); } return 0; } void* worker2(void* X) { pthread_mutex_lock(&EventMutex); pthread_cond_wait(&Event, &EventMutex); pthread_mutex_unlock(&EventMutex); for (int i = 1; i < 10; i++) { sleep(1); cout << "worker 2 locking" << endl; pthread_mutex_lock(&Mutex); Number += 20; cout << "worker 2 Number = " << Number << endl; pthread_mutex_unlock(&Mutex); } return 0; } int main() { pthread_mutex_init(&Mutex, NULL); pthread_mutex_init(&EventMutex, NULL); pthread_cond_init(&Event, NULL); pthread_create(&ThreadA, NULL, worker1, NULL); pthread_create(&ThreadB, NULL, worker2, NULL); pthread_join(ThreadA, NULL); pthread_join(ThreadB, NULL); return 0; }