什么是线程 线程是 CPU 调度的最小执行单位,你可以创建一个线程用于ListenMusic,再创建一个线程去PlayGame,这样操作系统就是同时处理这两个任务的(并发 )。
最直观的感受就是,你可以边打游戏边听歌。
Pthread库 使用说明 POSIX 标准定义了一套线程操作相关的函数,用于让程序员更加方便地操作管理线程,函数名都是以前缀 pthread_开始,使用前包含头文件
在链接的时候要手动链接 pthread 这个库,如:gcc main.c -lpthread -o main
常用函数 pthread_create():创建一个线程
pthread_self():获取线程id
int pthread_equal(pthread_t t1, pthread_t t2):比较两个线程是否完全相同
void pthread_exit(void *retval):退出指定进程
int pthread_join(pthread_t thread,void **retval):阻塞等待线程退出,获取线程退出状态,相当于进程中的 waitpid 函数,如果线程退出,pthread_join 立刻返回。
int pthread_detach(pthread_t thread):将线程 ID 为 thread 的线程分离出去,所谓分离出去就是指主线程不需要再通过 pthread_join 等方式等待该线程的结束并回收其线程控制块(TCB)的资源,被分离的线程结束后由操作系统负责其资源的回收。
int pthread_cancel(pthread_t thread):杀死线程
入门:Pthread实现多线程HelloWorld 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #include <pthread.h> #include <iostream> #include <stdlib.h> using namespace std;int thread_count;void * Hello (void * rank) ;int main (int argc,char * argv[]) { long thread; pthread_t * thread_handles; cin>>thread_count; thread_handles=(pthread_t *)malloc (thread_count*sizeof (pthread_t )); for (thread=0 ;thread<thread_count;thread++){ pthread_create (&thread_handles[thread],NULL ,Hello,(void *)thread); } cout<<"Hello from the main thread" <<endl; for (thread=0 ;thread<thread_count;thread++){ pthread_join (thread_handles[thread],NULL ); } free (thread_handles); return 0 ; } void * Hello (void * rank) { long my_rank=(long )rank; printf ("Hello from thread %ld of %d\n" ,my_rank,thread_count); }
1 2 3 4 5 6 7 输入:4 输出: Hello from thread 0 of 4 Hello from thread 1 of 4 Hello from thread 2 of 4 Hello from thread 3 of 4 Hello from the main thread
临界区 共享内存有一个基本问题,即当多个线程同时更新一个共享资源时,结果是无法预测的。比如一个加法进程与一个乘法进程同时访问一个共享资源,得到的结果可以完全不同:
应该保证临界区资源一次只允许一个线程执行代码片段 。
互斥量 互斥量类型:pthread_mutex_t
销毁:int pthread_mutex_destroy(pthread_mutex_t* mutex_p)
获取:int pthread_mutex_lock(pthread_mutex_t* mutex_p)
释放:int pthread_mutex_unlock(pthread_mutex_t* mutex_p)
用互斥量计算全局和
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 void * Thread_sum (void * rank) { long my_rank=(long )rank; double factor,my_sum=0.0 ; long long i; long long my_n=n/thread_count; long long my_first_i=my_n*my_rank; long long my_last_i=my)first_i+my_n; if (my_first_i%2 ==0 ){ factor=1.0 ; }else { factor=-1.0 ; } for (i=my_first_i;i<my_last_i;i++,factor=-factor) my_sum+=factor/(2 *i+1 ) pthread_mutex_lock (&mutex); sum+=my_sum; pthread_mutex_unlock (&mutex); }
生产者-消费者同步与信号量 生产者消费者同步模型
例:每个线程向其他线程发送消息
1-2,2-3,3-4,……(n-1)-n,n-0,类似于这样
使用忙等待 1 2 3 4 5 6 7 8 9 10 11 void * Send_msg (void * rank) { long my_rank=(long )rank; long desk=(my_rank+thread_count-1 )%thread_count; long source=(my_rank+thread_count-1 )%thread_count; char * my_msg=malloc (MSG*sizeof (char )); messages[dest]=my_msg; while (message[my_rank]==NULL ){ cout<<"Thread" <<my_rank<<message[my_rank]; } }
这里的忙等待与条件判断是大致一致的。
1 2 3 4 5 if (message[my_rank]!=NULL ){ cout<<"Thread" <<my_rank<<message[my_rank]; }else { cout<<"Thread" <<my_rank<<"No message from" <<source; }
信号量机制 可以类比一下操作系统中的P,V操作,将上述忙等待的内容更改成如下即可:
1 2 sem_post (&semphores[dest]);sem_wait (&semphores[my_rank]);
路障与条件变量 路障 路障指,使所有线程到达程序中同一个位置后,再继续执行。
实现方式1:忙等待 1 2 3 4 5 6 7 8 9 10 11 12 13 int counter=0 ;int thread_count;pthread_mutex_t barrier_mutex;void * Thread_work () { ... pthread_mutex_lock (&barrier_mutex); counter++; pthread_mutex_unlock (&barrier_mutex); while (counter<thread_count){...} }
实现方式2:信号量 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 int counter=0 ;sem_t barrier_sem=0 ;void * Thread_work () { ... sem_wait (&count_sem); if (counter==thread_count-1 ){ counter=0 ; sem_post (&count_sem); for (j=0 ;j<thread_count-1 ;j++){ sem_post (&barrier_sem); } }else { counter++; sem_post (&count_sem); sem_wait (&barrier_sem); } }
当路障重复使用时,barrier_sem产生竞争。