C语言多线程编程二
一. 线程通信----事件:
1.一对一模式:
#include <stdio.h> #include <stdlib.h> #include <Windows.h> //互斥解决线程冲突 //事件解决线程通信 //临界区解决线程冲突 //时间同步线程 HANDLE event[5] = { 0 }; HANDLE hd[5] = { 0 }; DWORD WINAPI Zhang(void *p) { int i = 1; printf("张%d次说:I love you Li.\n", i); Sleep(1000); SetEvent(event[1]); while (++i) { WaitForSingleObject(event[0], INFINITE); //无限等待一个信号 printf("张%d次说:I love you Li.\n", i); Sleep(1000); //ResetEvent(event[0]); //信号复位 SetEvent(event[1]); } return 0; } DWORD WINAPI Li(void *p) { int i = 0; while (++i) { WaitForSingleObject(event[1], INFINITE); //无限等待一个信号 printf("李%d次说:I love you too.\n", i); Sleep(1000); //ResetEvent(event[1]); //信号复位 SetEvent(event[0]); } return 0; } void main() { //第二个参数代表:自动FALSE、手动TRUE(需要reset) //第三个参数信号状态 //第四个参数标记名称 //event[0] = CreateEvent(NULL, TRUE, FALSE, NULL); //event[1] = CreateEvent(NULL, TRUE, FALSE, NULL); event[0] = CreateEvent(NULL, FALSE, FALSE, NULL); event[1] = CreateEvent(NULL, FALSE, FALSE, NULL); hd[0] = CreateThread(NULL, 0, Zhang, NULL, 0, NULL); hd[1] = CreateThread(NULL, 0, Li, NULL, 0, NULL); WaitForMultipleObjects(2, hd, TRUE, INFINITE); system("pause"); }
2. 一对一中介者模式:
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <Windows.h> //互斥解决线程冲突 //事件解决线程通信 //临界区解决线程冲突 //时间同步线程 HANDLE event[5] = { 0 }; HANDLE hd[5] = { 0 }; char str[256] = { 0 }; //全局变量,存放说的内容 DWORD WINAPI Wang(void *p) { int i = 0; int k = 0; //判断是哪个信号 while (++i) { if (k == 0) { WaitForSingleObject(event[1], INFINITE); //无限等待一个信号 printf("媒婆读取%d次:%s\n", i,str); Sleep(1000); SetEvent(event[2]); k = 1; } else { WaitForSingleObject(event[3], INFINITE); //无限等待一个信号 printf("媒婆读取%d次:%s\n", i, str); Sleep(1000); SetEvent(event[0]); k = 0; } } return 0; } DWORD WINAPI Zhang(void *p) { int i = 1; /*printf("张%d次说:I love you Li.\n", i);*/ memset(str, '0', 256); sprintf(str,"张%d次说:I love you Li.\n", i); Sleep(1000); SetEvent(event[1]); while (++i) { WaitForSingleObject(event[0], INFINITE); //无限等待一个信号 /*printf("张%d次说:I love you Li.\n", i);*/ memset(str, '0', 256); sprintf(str,"张%d次说:I love you Li.\n", i); Sleep(1000); //ResetEvent(event[0]); //信号复位 SetEvent(event[1]); } return 0; } DWORD WINAPI Li(void *p) { int i = 0; while (++i) { WaitForSingleObject(event[2], INFINITE); //无限等待一个信号 /*printf("李%d次说:I love you too.\n", i);*/ memset(str, '0', 256); sprintf(str,"李%d次说:I love you too.\n", i); Sleep(1000); //ResetEvent(event[1]); //信号复位 SetEvent(event[3]); } return 0; } void main() { //第二个参数代表:自动FALSE、手动TRUE(需要reset) //第三个参数信号状态 //第四个参数标记名称 //event[0] = CreateEvent(NULL, TRUE, FALSE, NULL); //event[1] = CreateEvent(NULL, TRUE, FALSE, NULL); event[0] = CreateEvent(NULL, FALSE, FALSE, NULL); event[1] = CreateEvent(NULL, FALSE, FALSE, NULL); event[2] = CreateEvent(NULL, FALSE, FALSE, NULL); event[3] = CreateEvent(NULL, FALSE, FALSE, NULL); hd[0] = CreateThread(NULL, 0, Zhang, NULL, 0, NULL); hd[1] = CreateThread(NULL, 0, Li, NULL, 0, NULL); hd[2] = CreateThread(NULL, 0, Wang, NULL, 0, NULL); WaitForMultipleObjects(3, hd, TRUE, INFINITE); system("pause"); }
3. 一对多广播模式:
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <Windows.h> //互斥解决线程冲突 //事件解决线程通信 //临界区解决线程冲突 //时间同步线程 HANDLE event[5] = { 0 }; HANDLE hd[10] = { 0 }; char ch[10] = { 'A','B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J' }; char str[256] = { 0 }; DWORD WINAPI ZhangGirlFriend(void *p) { char *pch = p; printf("I am %c ZhangGirlFrend.\n", *pch); if (*pch == 'A') { MessageBoxA(0, "1", "1", 0); sprintf(str,"张女友-%c speak: xiaohaha \n", *pch); SetEvent(event[0]); } int i = 0; while (++i) { WaitForSingleObject(event[0], INFINITE); printf("ZhangGirlFriend-%c read %s\n", *pch, str); Sleep(1000); ResetEvent(event[0]); } return 0; } void main() { //第二个参数代表:自动FALSE(收到一次自动清空一次)、手动TRUE(需要reset) //第三个参数信号状态 //第四个参数标记名称 event[0] = CreateEventA(NULL, TRUE, FALSE, "msg"); //一直等待消息 for (int i = 0; i < 10; i++) { hd[i] = CreateThread(NULL, 0, ZhangGirlFriend, &ch[i], 0, NULL); } WaitForMultipleObjects(10, hd, TRUE, INFINITE); system("pause"); }
二. 信号量:
1. 信号量用作“关卡”的作用:
//信号量-->关卡的作用 #include <stdio.h> #include <stdlib.h> #include <Windows.h> #define id "Zhang" #define MAX 3 //0 无限等待 DWORD WINAPI myworker(void *p) { int *pint = p; printf("myworker%d is running...\n", *pint); HANDLE myhsem = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, id); //打开一个信号 if (myhsem) { printf("myworker%d is waiting...\n", *pint); //初始时信号为0,为0就会死锁,信号量不减 //不为0的情况下,信号量-1 if (WaitForSingleObject(myhsem, INFINITE) == WAIT_OBJECT_0) //等到了信号 { printf("myworker%d is getting.\n", *pint); Sleep(3000); printf("myworker%d is leaving.\n", *pint); ReleaseSemaphore(myhsem, 1, NULL);//释放资源 +1 CloseHandle(myhsem);//执行完成退出 } } return 1; } void main() { //创建信号对象 HANDLE hSEM = CreateSemaphore(NULL, 0, MAX, id);//开辟一个信号,最大计数是3 int a[10] = { 0,1,2,3,4,5,6,7,8,9 }; HANDLE hd[10] = { 0 }; for (int i = 0; i < 10; i++) { hd[i] = CreateThread(NULL, 0, myworker, a + i, 0, NULL); //创建10个线程 } Sleep(5000); printf("激活线程.\n"); ReleaseSemaphore(hSEM, MAX, NULL);//最多一次放过3个 +3 WaitForMultipleObjects(10, hd, TRUE, INFINITE); CloseHandle(hSEM); system("pause"); }
2. 信号量实现互斥:
#include <stdio.h> #include <stdlib.h> #include <Windows.h> int num = 0; //互斥量,只能让一个线程运行,其他休眠 //信号量,可以让多个线程运行,其他线程休眠 //临界区,只能让一个线程运行,其他休眠 //原子操作,操作速度最快 //事件也可以实现互斥 DWORD WINAPI add(void *p) { HANDLE myhsem = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, "Hello"); if (myhsem) { if (WaitForSingleObject(myhsem, INFINITE) == WAIT_OBJECT_0) //等到了信号 { for (int i = 0; i < 10000; i++) { num++; } ReleaseSemaphore(myhsem, 1, NULL);//释放资源 +1 CloseHandle(myhsem);//执行完成退出 } } else { printf("信号量获取失败.\n"); } return 1; } void main() { HANDLE hSEM = CreateSemaphore(NULL, 0, 1, "Hello"); HANDLE hd[64] = { 0 }; for (int i = 0; i < 64; i++) { hd[i] = CreateThread(NULL, 0, add, NULL, 0, NULL); //创建64个线程 } Sleep(2000); printf("激活线程.\n"); ReleaseSemaphore(hSEM, 1, NULL); //一次放过一个 WaitForMultipleObjects(64, hd, TRUE, INFINITE); printf("%d\n", num); CloseHandle(hSEM); system("pause"); }
三. 互斥锁:
相关函数如下:
第一个 InitializeSRWLock
函数功能:初始化读写锁
函数原型:VOID InitializeSRWLock(PSRWLOCK SRWLock);
函数说明:初始化(没有删除或销毁SRWLOCK的函数,系统会自动清理)
第二个 AcquireSRWLockExclusive
函数功能:写入者线程申请写资源。
函数原型:VOID AcquireSRWLockExclusive(PSRWLOCK SRWLock);
第三个 ReleaseSRWLockExclusive
函数功能:写入者线程写资源完毕,释放对资源的占用。
函数原型:VOID ReleaseSRWLockExclusive(PSRWLOCK SRWLock);
第四个 AcquireSRWLockShared
函数功能:读取者线程申请读资源。
函数原型:VOID AcquireSRWLockShared(PSRWLOCK SRWLock);
第五个 ReleaseSRWLockShared
函数功能:读取者线程结束读取资源,释放对资源的占用。
函数原型:VOID ReleaseSRWLockShared(PSRWLOCK SRWLock);
#include <stdio.h> #include <stdlib.h> #include <Windows.h> int num = 6400000; SRWLOCK g_lock; //注意一个线程仅能锁定资源一次,不能多次锁定资源。 DWORD WINAPI read(void *p) { AcquireSRWLockShared(&g_lock); //读取期间锁定数据,数据无法被修改 int i = 0; while (1) { Sleep(1000); printf("第%d秒num=%d\n", i, num); if (i == 10) { break; } i++; } ReleaseSRWLockShared(&g_lock); return 1; } //改变一个变量的时候需要锁定 DWORD WINAPI write(void *p) { AcquireSRWLockExclusive(&g_lock); //锁定写入 printf("开始写入...\n"); for (int i = 0; i < 100000; i++) { num--; //Sleep(10); } ReleaseSRWLockExclusive(&g_lock); printf("结束写入...\n"); return 1; } void main() { InitializeSRWLock(&g_lock); //初始化互斥锁 CreateThread(NULL, 0, read, NULL, 0, NULL); HANDLE hd[64]; for (int i = 0; i < 64; i++) { hd[i] = CreateThread(NULL, 0, write, NULL, 0, NULL); } WaitForMultipleObjects(64, hd, TRUE, INFINITE); printf("last=%d\n", num); system("pause"); }
四. 跨进程通信:
1. 信号量mutex 跨进程通信:
文件mutex1.c :
#include <stdio.h> #include <stdlib.h> #include <Windows.h> char name[100] = "Zhang love Li"; void main() { HANDLE mutex = OpenMutexA(MUTEX_ALL_ACCESS, TRUE, name); if (mutex == NULL) { printf("打开失败!\n"); system("pause"); return; } printf("等待------\n"); DWORD res = WaitForSingleObject(mutex, 10000); switch (res) { case WAIT_OBJECT_0: printf("收到信号---\n"); break; case WAIT_TIMEOUT: printf("超时没有收到---\n"); break; case WAIT_ABANDONED: printf("另外一个进程意外终止---\n"); break; default: break; } CloseHandle(mutex); system("pause"); }
文件mutex2.c :
#include <stdio.h> #include <stdlib.h> #include <Windows.h> char name[100] = "Zhang love Li"; void main() { HANDLE mutex = CreateMutexA(NULL, TRUE, name); printf("创建成功!\n"); char ch = getchar(); ReleaseMutex(mutex); //离开互斥区 printf("触发互斥量.\n"); CloseHandle(mutex); system("pause"); }
运行结果:
2. 事件 event 跨进程通信:
文件event.c :
#include <stdio.h> #include <stdlib.h> #include <Windows.h> char name[100] = "Zhang love Li"; //只有mutex可以感知丢失,event无法感知 void main() { HANDLE event = CreateEventA(NULL, FALSE, FALSE, name); printf("创建成功!\n"); char ch = getchar(); SetEvent(event); printf("触发event.\n"); CloseHandle(event); system("pause"); }
文件 wait.c :
#include <stdio.h> #include <stdlib.h> #include <Windows.h> char name[100] = "Zhang love Li"; void main() { HANDLE event = OpenEventA(EVENT_ALL_ACCESS, TRUE, name);//打开事件 if (event == NULL) { printf("打开失败!\n"); system("pause"); return; } printf("等待------\n"); DWORD res = WaitForSingleObject(event, 10000); switch (res) { case WAIT_OBJECT_0: printf("收到信号---\n"); break; case WAIT_TIMEOUT: printf("超时没有收到---\n"); break; case WAIT_ABANDONED: printf("另外一个进程意外终止---\n"); break; default: break; } CloseHandle(event); system("pause"); }
3. 信号 semaphore 跨进程通信:
semaphore.c :
#include <stdio.h> #include <stdlib.h> #include <Windows.h> char name[100] = "Zhang love Li"; //只有mutex可以感知丢失,event无法感知 void main() { HANDLE hsem = CreateSemaphoreA(NULL, 0, 1, name); printf("创建成功!\n"); char ch = getchar(); ReleaseSemaphore(hsem, 1, NULL); printf("触发信号量semaphore.\n"); CloseHandle(hsem); system("pause"); }
wait.c :
#include <stdio.h> #include <stdlib.h> #include <Windows.h> char name[100] = "Zhang love Li"; void main() { HANDLE hsem = OpenSemaphoreA(SEMAPHORE_ALL_ACCESS, TRUE, name); if (hsem == NULL) { printf("打开失败!\n"); system("pause"); return; } printf("等待------\n"); DWORD res = WaitForSingleObject(hsem, 10000); switch (res) { case WAIT_OBJECT_0: printf("收到信号---\n"); break; case WAIT_TIMEOUT: printf("超时没有收到---\n"); break; case WAIT_ABANDONED: printf("另外一个进程意外终止---\n"); break; default: break; } CloseHandle(hsem); system("pause"); }
五. 回调函数与定时器:
#include <stdio.h> #include <stdlib.h> #include <Windows.h> //回调函数,函数指针可以来调用 VOID CALLBACK timerun(void *parg, DWORD timearg, DWORD timehigh) { DWORD dwindex = *(DWORD *)parg; printf("第%d次\n", dwindex); } void main() { HANDLE timer1 = CreateWaitableTimerA(NULL, TRUE, "hello");//创建时钟 if (timer1 == NULL) { printf("创建失败!\n"); } LARGE_INTEGER mytime; mytime.QuadPart = -50000000; //单位是0.1微秒 DWORD dwparam = 1; //设置定时器 if (SetWaitableTimer(timer1, &mytime, 1000, timerun, &dwparam, FALSE))//1000 1秒循环一次 { printf("等待5秒后开始干活!\n"); for (int i = 0; i < 15; i++, dwparam++) //循环调用多少次 { SleepEx(INFINITE, TRUE); } } CancelWaitableTimer(timer1);//取消定时器 CloseHandle(timer1); system("pause"); }
六. 原子操作:
#include <stdio.h> #include <stdlib.h> void main() { volatile int i = 10; //数据被意外改变的时候,强制读内存 int a = i; printf("i=%d\n", a); //偷偷改变i __asm { mov dword ptr[ebp-4],20h //16进制 20h=32 } int b = i; printf("i=%d\n", b); system("pause"); }
上面结果在Debug模式下结果为:
在Release模式下结果为: