当有多个线程的时候,常常须要去同步这些线程以访问同一个数据或资源。例如,假设有一个程序,其中一个线程用于把文件读到内存,而另外一个线程用于统计文件中的字符数。固然,在把整个文件调入内存以前,统计它的计数是没有意义的。可是,因为每一个操做都有本身的线程,操做系统会把两个线程看成是互不相干的任务分别执行,这样就可能在没有把整个文件装入内存时统计字数。为解决此问题,你必须使两个线程同步工做。html
所谓同步,是指在不一样进程之间的若干程序片段,它们的运行必须严格按照规定的某种前后次序来运行,这种前后次序依赖于要完成的特定的任务。若是用对资源的访问来定义的话,同步是指在互斥的基础上(大多数状况),经过其它机制实现访问者对资源的有序访问。在大多数状况下,同步已经实现了互斥,特别是全部写入资源的状况一定是互斥的。少数状况是指能够容许多个访问者同时访问资源。linux
所谓互斥,是指散布在不一样进程之间的若干程序片段,当某个进程运行其中一个程序片断时,其它进程就不能运行它们之中的任一程序片断,只能等到该进程运行完这个程序片断后才能够运行。若是用对资源的访问来定义的话,互斥某一资源同时只容许一个访问者对其进行访问,具备惟一性和排它性。但互斥没法限制访问者对资源的访问顺序,即访问是无序的。ios
线程间的同步方法大致可分为两类:用户模式和内核模式。顾名思义,内核模式就是指利用系统内核对象的单一性来进行同步,使用时须要切换内核态与用户态,而用户模式就是不须要切换到内核态,只在用户态完成操做。编程
用户模式下的方法有:原子操做(例如一个单一的全局变量),临界区。windows
内核模式下的方法有:事件,信号量,互斥量。安全
一、临界区:经过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。
二、互斥量:为协调共同对一个共享资源的单独访问而设计的。
三、信号量:为控制一个具备有限数量用户资源而设计。
四、事 件:用来通知线程有一些事件已发生,从而启动后继任务的开始。网络
(1)管道(pipe)及有名管道(named pipe):管道可用于具备亲缘关系的父子进程间的通讯,有名管道除了具备管道所具备的功能外,它还容许无亲缘关系进程间的通讯。多线程
(2)信号(signal):信号是在软件层次上对中断机制的一种模拟,它是比较复杂的通讯方式,用于通知进程有某事件发生,一个进程收到一个信号与处理器收到一个中断请求效果上能够说是一致的。socket
(3)消息队列(message queue):消息队列是消息的连接表,它克服了上两种通讯方式中信号量有限的缺点,具备写权限得进程能够按照必定得规则向消息队列中添加新信息;对消息队列有读权限得进程则能够从消息队列中读取信息。ide
(4)共享内存(shared memory):能够说这是最有用的进程间通讯方式。它使得多个进程能够访问同一块内存空间,不一样进程能够及时看到对方进程中对共享内存中数据得更新。这种方式须要依靠某种同步操做,如互斥锁和信号量等。
(5)信号量(semaphore):主要做为进程之间及同一种进程的不一样线程之间得同步和互斥手段。
(6)套接字(socket):这是一种更为通常得进程间通讯机制,它可用于网络中不一样机器之间的进程间通讯,应用很是普遍。
头文件 <windows.h>
建立线程API:
1 HANDLE CreateThread( 2 __in SEC_ATTRS 3 SecurityAttributes, //线程安全属性 4 __in ULONG 5 StackSize, // 堆栈大小 6 __in SEC_THREAD_START 7 StartFunction, // 线程函数 8 __in PVOID 9 ThreadParameter, // 线程参数 10 __in ULONG 11 CreationFlags, // 线程建立属性 12 __out PULONG 13 ThreadId // 线程ID 14 );
使用步骤:
HANDLE hThread = CreateThread(NULL, 0, Fun, NULL, 0, NULL); CloseHandle(hThread);
互斥API:互斥量实现
1 HANDLE CreateMutex( 2 LPSECURITY_ATTRIBUTES lpMutexAttributes, 3 BOOL bInitialOwner, // 指定该资源初始是否归属建立它的进程 4 LPCTSTR lpName // 指定资源的名称 5 ); 6 7 BOOL ReleaseMutex( 8 HANDLE hMutex // 该函数用于释放一个独占资源,进程一旦释放该资源,该资源就再也不属于它了 9 ); 10 11 DWORD WaitForSingleObject( 12 HANDLE hHandle, // 指定所申请的资源的句柄 13 DWORD dwMilliseconds // 通常指定为INFINITE,表示若是没有申请到资源就一直等待该资源 14 );
互斥API:临界区实现
1 CRITICAL_SECTION cs; 2 InitializeCriticalSection(&cs); 3 EnterCriticalSection(&cs); 4 LeaveCriticalSection(&cs); 5 DeleteCriticalSection(&cs);
例子:
1 #include <iostream> 2 #include <windows.h> 3 using namespace std; 4 5 HANDLE hMutex; 6 7 DWORD WINAPI Fun(LPVOID lpParamter) 8 { 9 while (1) { 10 WaitForSingleObject(hMutex, INFINITE); 11 cout << "Fun display!" << endl; 12 Sleep(1000); 13 ReleaseMutex(hMutex); 14 } 15 } 16 17 int main() 18 { 19 HANDLE hThread = CreateThread(NULL, 0, Fun, NULL, 0, NULL); 20 hMutex = CreateMutex(NULL, FALSE, "screen"); 21 CloseHandle(hThread); 22 while (1) { 23 WaitForSingleObject(hMutex, INFINITE); 24 cout << "main display!" << endl; 25 Sleep(2000); 26 ReleaseMutex(hMutex); 27 } 28 29 return 0; 30 }
头文件<phread.h>
建立线程API:
1 int pthread_create(pthread_t *tidp, //第一个参数为指向线程标识符的指针。 2 const pthread_attr_t *attr, //第二个参数用来设置线程属性。 3 (void*)(*start_rtn)(void*), //第三个参数是线程运行函数的起始地址。 4 void *arg //最后一个参数是运行函数的参数 5 ); 6 7 //线程经过调用pthread_exit函数终止执行 8 void pthread_exit(void* retval); 9 10 //函数pthread_join用来等待一个线程的结束,线程间同步的操做,阻塞函数 11 int pthread_join(pthread_t thread, void **retval);
例子:
1 #include <stdio.h> 2 #include <pthread.h> 3 #include <sched.h> 4 5 static int run = 1; 6 static int retvalue; 7 8 void *start_routine(void *arg) 9 { 10 int *running = arg; 11 printf("子线程初始化完毕,传入参数为:%d\n", *running); 12 while (*running) 13 { 14 printf("子线程正在运行\n"); 15 usleep(1); 16 } 17 printf("子线程退出\n"); 18 19 retvalue = 8; 20 pthread_exit((void*)&retvalue); 21 } 22 23 int main(void) 24 { 25 pthread_t pt; 26 int ret = -1; 27 int times = 3; 28 int i = 0; 29 int *ret_join = NULL; 30 31 ret = pthread_create(&pt, NULL, (void*)start_routine, &run); 32 if (ret != 0) 33 { 34 printf("创建线程失败\n"); 35 return 1; 36 } 37 usleep(1); 38 for (; i < times; i++) 39 { 40 printf("主线程打印\n"); 41 usleep(1); 42 } 43 run = 0; 44 pthread_join(pt, (void*)&ret_join); 45 printf("线程返回值为:%d\n", *ret_join); 46 return 0; 47 48 }
互斥步骤:
互斥锁:等价于二元信号量
注意:锁分为递归锁和非递归锁,递归锁能够对自身加锁的锁加锁屡次。
pthread_mutex_t mutex; pthread_mutex_init(&mutex, NULL); pthread_mutex_lock(&mutex); pthread_mutex_unlock(&mutex);
生产者消费者例子:(仅仅互斥,没有用信号量同步)
1 /* 2 * ex04-mutex.c 3 * 线程实例 4 */ 5 #include <stdio.h> 6 #include <pthread.h> 7 #include <sched.h> 8 9 10 void *producter_f (void *arg); 11 void *consumer_f (void *arg); 12 13 14 int buffer_has_item=0; 15 pthread_mutex_t mutex; 16 17 int running =1 ; 18 19 int main (void) 20 { 21 pthread_t consumer_t; 22 pthread_t producter_t; 23 24 pthread_mutex_init (&mutex,NULL); 25 26 pthread_create(&producter_t, NULL,(void*)producter_f, NULL ); 27 pthread_create(&consumer_t, NULL, (void *)consumer_f, NULL); 28 usleep(1); 29 running =0; 30 pthread_join(consumer_t,NULL); 31 pthread_join(producter_t,NULL); 32 pthread_mutex_destroy(&mutex); 33 34 return 0; 35 } 36 37 void *producter_f (void *arg) 38 { 39 while(running) 40 { 41 pthread_mutex_lock (&mutex); 42 buffer_has_item++; 43 printf("生产,总数量:%d\n",buffer_has_item); 44 pthread_mutex_unlock(&mutex); 45 } 46 } 47 48 void *consumer_f(void *arg) 49 { 50 while(running) 51 { 52 pthread_mutex_lock(&mutex); 53 buffer_has_item--; 54 printf("消费,总数量:%d\n",buffer_has_item); 55 pthread_mutex_unlock(&mutex); 56 } 57 }
同步步骤:信号量 头文件<semaphore.h>
参见《Linux网络编程》
参见:C11多线程