程序片段(01):01.多线程.c+02.多线程操作.c
内容概要:多线程
///01.多线程.c
#include
#include
#include
#include
//01.线程任务函数剖析:
// 1."封装"线程任务代码
// 2.MessageBox();作用:
// 用于"阻塞"当前线程的继续执行状态
// 也就是暂停线程任务代码的执行
//02.MessageBox();带参宏的作用:
// 1.弹出一个消息盒子
// 2.该带参宏位于那条线程任务函数当中,就会在
// 该线程之上产生同步效果,从而阻塞该条线程
//注:MessageBox();带参宏和TEXT();带参宏结合可以
// 解决字符集兼容性问题(让任何文字自动使用本地字符集)
void thTaskFun(void * pArr)
{
MessageBox(0, TEXT("1"), TEXT("1"), 0);
}
//03.关于开启线程函数(_beginthread();)函数的解析:
// 1.头文件:process.h
// 2.格式:_beginthread(arg1, arg2, arg3);
// 3.参数:
// arg1:函数指针(表象)<->线程任务代码(实质)
// arg2:栈的尺寸,0表示默认情况(拷贝主线程栈尺寸作为新开启线程所占用的栈尺寸)
// arg3:开启线程之后,向线程任务函数所传递的参数(数据封装体的地址)
//注:数据封装体的地址,便于跨函数访问多个参数!+提升效率
int main01(void)
{
MessageBox(0, "2", "2", 0);//此处将会阻塞主线程
_beginthread(thTaskFun, 0, NULL);//用于开启一条异步执行的线程
_beginthread(thTaskFun, 0, NULL);
_beginthread(thTaskFun, 0, NULL);
_beginthread(thTaskFun, 0, NULL);
system("pause");
}
///02.多线程操作.c
#include
#include
#include
#include
//01.线程状态管理:
// 线程状态:冻结(暂停)+解冻(继续)
//注:_beginthread();不太注重代码格式要求,CreateThread();注重代码格式要求
void print(void * p)
{
int i = 0;
while (1)
{
printf("%d \n", ++i);
Sleep(1000);
}
}
//02.阻塞线程和阻塞进程:
// 阻塞线程
// 1.阻塞线程之后,只是当前线程的后续代码暂停执行状态
// 2.任何情况之下都可以触发阻塞线程的状态
// 阻塞进程:
// 1.阻塞进程之后,进程之内的所有代码全部处于暂停执行执行状态
// 2.CPU在为单个进程只开启单条线程的情况之下才能出现阻塞进程现象
//注:Debug调试模式状态下,无论是单线程还是多线程,只要阻塞了主线程
// 就会出现阻塞进程的现象,从而其它线程无法得到具体的执行
//注:只有在多线程的情况之下才具备阻塞线程和阻塞进程之间的区别
// 因为在单线程的情况之下阻塞线程就等同于阻塞进程
//注:阻塞主线程的情况之下,才有可能阻塞进程,从而让其它所有的代码得不到
// 继续执行的机会(当然,是在其他线程没有启动的情况之下)
//注:冻结主线程的情况之下,在调试模式下等同于阻塞进程,在这种情况之下可以
// 方便与实施对其他线程的状态切换操作
//03.阻塞线程的两种常见方式:
// MessageBox();带参宏
// system("pause");
int main02(void)
{
_beginthread(print, 0, NULL);
system("pause");//(Debug调试模式下)暂停主线程等同于暂停整个进程,便于对其它线程的状态切换操作
system("pause");
system("pause");
system("pause");
}
//01.线程状态:冻结(暂停)&解冻(继续)
//02.线程休眠:Sleep();用于当前线程任务函数之中,休眠当前线程
//03.异步线程:_beginthread(run,0,NULL);
// run:函数指针
// 0:堆栈尺寸
// NULL:参数列表
//04.阻塞线程:
// MessageBox();
// system("pause");
//05.冻结&解冻[线程管理方面的重要概念]
// 实质:在于是否存在于系统所控制的环形队列当中<=>是否处于等待CPU执行的状态
// 特点:由于CPu存在的固定的CPU执行频率,因此CPU对环形队列当中的线程任务分配
// 相应所需的时间片,按照顺序进行环形队列当中的线程任务函数内容切换
//注:一条线程对应于一个环形队列,对应于一组线程任务序列
//06.操作系统管理线程的特点:
// 1.操作系统为每个程序的相应代码片段分配相应的时间片
// 2.该时间片模型的本质就是一个环形队列(线程<->队列)
// 3.CPU针对于每一个时间片进行切换访问
//07.关于同步与异步的概念:
// 1.单条线程根本不存在同步与异步的概念,只是会出现同步与异步的现象
// 2.只有多线程的情况之下,才存在同步与异步的概念
//注:严格区分概念和现象
//08.如何进行线程冻结&解冻状态的演示?
// 1.采用两条线程
// 2.主线程的阻塞状态会导致副线程暂时无法获得CPU的执行权
// 3.在主线程的阻塞状态之下进行副线程的线程状态管理演示[冻结&解冻]
//09.测试多线程:多线程的常用操作
// 例子-->细节-->通信-->队列
//10.操作系统特点:
// 1.操作系统:PC&Phone
// 2.由于CPU存在CPU执行频率,所以操作系统分配给线程的任务代码时间片大小有限,
// 也就是环形队列总时间片固定,只不过是循环进行执行,所以应用程序不会存在执
// 行不到的情况[CPU刷新环形队列的频率]
// 3.单核CPU一个时刻只会执行一条线程,根据概率进行切换
// 4.关于多线程的假象问题:
// 单核CPU只能出现多线程的假象情况,不会出现多多线程的真相情况
// 也就是说某一时刻,单核CPU只会处理一条线程,多核CPU才能出现处理多线程情况
// 5.操作系统通过线程环形队列实现对CPU随机切换动作的调度[随机性]
// 6.所有的线程都放置在同一个操作系统所管理的环形队列当中,然后让操作系统对CPU进行随机调度执行
// CPU调度执行某条线程-->从而指向某条线程的任务代码
//11.冻结&解冻:
// 冻结:休眠&从环形队列当中退出
// 解冻:运行&进入到环形队列当中-->等待执行(不一定立即得到CPU的执行权)
// 执行权:CPU切换到线程的执行状态-->执行
程序片段(02):01._beginthread.c+02.CreateThread.c
内容概要:多线程冲突问题
///01._beginthread.c
#include
#include
#include
#include
#include
//01.临界区:CRITICAL_SECTION
// 1.解决多线程并发访问冲突问题
// 2.所允许的多线程并发访问数64
// 3.通常情况之下需要将临界区定义为全局变量:
// 为的是让多条线程识别同一个临界区!(防止互锁)
//注:临界区情况之下,如果并发访问数目大于64
// 那么多线程并发访问效果将会失效
//注:临界区的实质是一个结构体类型,它位于Windows.h
// 头文件中进行的类型定义
//注:C语言的全局变量不会进行自动初始化操作,CPP语言
// 会进行默认的自动初始化操作
static CRITICAL_SECTION cs;
//02.全局变量:用于多线程之间的通信信息描述
// 任何一个当前进程下的线程都可以访问这个全局变量
//注:全局变量在默认的情况之下,如果被多条线程所并发
// 访问,容易导致线程并发访问冲突问题!
static int num = 0;
//03.多线程并发访问问题详细剖析:
// 1.理论上:100条线程并发计算同一个数据之后的结果应当是
// 100*100=10000,但是结果却是达不到10000
// 2.原因是:多条线程同时读取到相同的数据,再进行数据写入
// 的时候,写入相同的结果,因此导致计算次数-1,因此结果-1
//注:使用临界区之后,多条线程在同一时刻的情况之下,只能由
// 单条线程进行数据的访问(读取和写入)
//注:使用临界区之后,将会有N多条线程处于运行状态下:
// 其中,(N-1)条处于运行"等待"状态下-->
// 全部都是处于"急切"等待状态下-->
// 临界区一旦接触,就会随机抽取一条线程得到CPU的执行权
// 其中,1条处于运行"执行"状态下
//注:避免频繁的使用临界区可以加快数据的访问效率
// 严格避免频发的加解锁情况发生(多线程效率提升与优化)
static void myFun(void * p)
{
for (int i = 0; i < 100; ++i)
{
EnterCriticalSection(&cs);//进入临界区
++num;
LeaveCriticalSection(&cs);//离开临界区
Sleep(3);//不要讲时间操作放置于临界区当中(否则极度耗费时间)
}
}
//04.如何对程序进行计时操作?
// 1.时间刻度
// 2.记录开始
// 3.记录结束
// 4.时间差值
//05.HANDLE类型解析:
// 1.位于Windows.h头文件当中
// 2.名为句柄,实质void *,也就是一个空类型的地址类型
// 只是一个地址数值的类型定义,没有明确的解析方式
//06.要想模拟多线程处理操作,必须等待所有线程执行完毕
// 最终再进行多条线程的结果处理
//07.等待多条线程执行状态的结束:
// 格式:WaitForMutipleObjects(arg1, arg2, arg3, arg4);
// 参数:arg1:线程个数+arg2:线程数组+arg3:单个|全部等待+arg4:等待时间
//08.这儿处在两个冲突问题:
// 1.同步执行:结果精确,耗费时间
// 2.异步执行:时间精确,结果不正确
//09.解决方式:
// 临界区:实质就是加解锁的特点
// 添加一把锁,必须等待当前线程执行状态完毕之后,再进行解锁,再让下一个
// 线程执行临界区当中的代码块儿
// 信号量:通过信号提示决定是否可以进行变量的访问
int main01(void)
{
InitializeCriticalSection(&cs);
time_t start, end;
time(&start);
HANDLE thArr[100] = { 0 };
for (int i = 0; i < 100; ++i)
{//这里需要等待所有线程执行结束并退出,所以需要使用线程句柄数组进行统一管理
thArr[i] = _beginthread(myFun, 0, NULL);
//thArr[i] = CreateThread(NULL, 0, myFun, NULL, 0, NULL);
//这儿如果是使用等待单条线程执行完毕之后,再让下一个线程执行,这样会导致等待时间较长
//效率低下,等待一个一个的线程执行结束,如果修改成功,同步收取,一个一个的执行,防止竞争
//关系的产生,造成最终的处理结果丢失
}
WaitForMultipleObjects(100, thArr, TRUE, INFINITE);
time(&end);
printf("difftime = %lf \n", difftime(end, start));
printf("num = %d \n", num);
DeleteCriticalSection(&cs);
system("pause");
}
//01.多线程问题:冻结&解冻之后
// 1.通过队列,通过一些额外的方式对多线程进行一些额外的操作
// 2.多线程冲突问题:开发中间经常遇到的问题
// 多条线程同时访问一个全局变量,容易出现多线程并发访问问题
//02.全局变量:
// 任何一条线程都可以进行该变量的访问,所以多线程在访问的时候,非常容易出问题,所以在进行大数据处理
// 的时候,将所有的数据全部加载进内存之后,如果采用多线程并发访问,就需要首要解决这个问题!
//03.解决多线程并发访问问题:
// 1.同步单条线程:缺点就是单条线程的操作时间过长!->效率低下,耗费时间
// 2.因此需要使用临界区进行各条不同的线程之间的同一时刻的屏蔽操作
//04.计时函数编程:
// 1.#include
// 2.定义->起始(时刻)->终止(时刻)->差距->显示
// time_t start,end;
// time(&start);//记录起始
// time(&end);//记录结束
// difftime(end,start);//统计差距
// %lf//显示结果
//05.定义临界区:
// 1.修改后缀为.cpp
// 2.定义步骤:
// 声明临界区:
// CRITICAL_SECTION cs;//实质就是一个"结构体"
// 解释:LONG LockCount;LONG RecursionCount;[Lock信号+方式]
// 特点:该临界区既可以应用于C++同样也可以应用于C当中
// 原则:一般情况之下,我们应用多线程都是采用CPP
// 进入临界区:
// EnterCriticalSection(&cs);
// 离开临界区:
// LeaveCriticalSection(&cs);
// 删除临界区:
// 特点:临界区不是一个变量,实质上是操作系统,让我们的这个线程进入到临界区锁定状态
// 临界区位于Windows.h头文件当中(多线程最高级是由操作系统完成调度的)
// DeleteCriticalSection(&cs);
//06.临界区原理:保证精确操作
// 1.并行操作情况之下,效率提升,但是结果不精确-->推出临界区概念:
// 临界区用于解决同步单条线程的效率低下问题,线程进入临界区之后,该条线程独享代码执行权
// 2.临界区的概念:媒婆特点
//07.CreateThread();与_beginthread();的区别
// CreateThread();使用的是HANDLE
// _beginthead();使用的是int
// 属于process.h线程库当中的一个线程函数声明,这里只能给你发生互锁的情况,但是不能解决多线程的冲突问题,所以这儿需要使用
// CreateThead();线程访问的时候发生的冲突情况[只能加锁,弹出多线程访问异常]
//08.代码调试的方式:
// 逐步放开部分代码进行观察
///02.CreateThread.c
#include
#include
#include
#include
//01.定义"临界区"为全局变量:
// 原因:让多条线程识别同一个临界区
CRITICAL_SECTION cs;
//02.定义"多线程"的通信信息:
// 多线程信息容易导致多线程并发访问冲突问题
int num = 0;
//03._beginthread();线程任务函数格式
//void myFun(void * p)
//{
// for (int i = 0; i < 100; ++i)
// {
// EnterCriticalSection(&cs);
// ++num;
// LeaveCriticalSection(&cs);
// }
//}
//04.CreateThread();线程任务函数格式
// DWORD:unsigned long
// WINAPI:声明让操作系统来调度这条线程[操作系统调度标识]
DWORD WINAPI myFun(void * p)
{
EnterCriticalSection(&cs);
for (int i = 0; i < 100; ++i)
{
++num;
}
LeaveCriticalSection(&cs);
return 0;
}
int main02(void)
{
InitializeCriticalSection(&cs);
time_t start, end;
time(&start);
HANDLE thArr[100] = { 0 };
for (int i = 0; i < 100; ++i)
{
thArr[i] = CreateThread(NULL, 0, myFun, NULL, 0, NULL);
//WaitForSingleObject(thArr[i], INFINITE);
}
WaitForMultipleObjects(100, thArr, TRUE, INFINITE);
time(&end);
printf("difftime = %lf \n", difftime(end, start));
printf("num = %d \n", num);
DeleteCriticalSection(&cs);
system("pause");
}
//01.临界区:
// CRITICAL_SECTION:处于"内核部位",它的实现是依赖于操作系统的"内核"
// 所以这里创建线程的方式使用CreateThread();[系统特点&HANDLE]
//02.由于CreateThread();函数来源于Windows内部,所以该函数的参数会很多,于是进行函数改良
// 声明:DWORD WINAPI
// 创建:CreateThread(NULL,0,myfun,NULL,0,NULL);
// NULL:附加信息,安全等级
// 0:堆栈大小:
// myfun:函数指针
// NULL:参数列表
// 0:优先等级
// NULL:线程编号
//03.这儿的临界区还是存在一定的问题
程序片段(03):_beginthread.c
内容概要:线程池
#include
#include
#include
#include
//01.创建线程的两种方式:
// 实质:CreateThread();
// 表象:_beginthread();
//02.结束线程的三种方式:
// 内部:
// endthread();
// exitthread();
// 外部:
// terminate();
//03.线程状态的两种控制方式:
// 冻结:SuspendThread(hd);
// 解冻:ResumeThread(hd);
//04.CreateThread();创建线程所需线程函数格式:
// DWORD:unsigned long
DWORD WINAPI myFun(void * p)
{
int i = 0;
while (++i)
{
printf("%d \n", i);
if (i > 8000)
_endthread();
Sleep(1000);
}
return 0;
}
//05.主线程与辅助线程:
// 1.主线程:处理主函数的线程
// 在整个进程当中起到主导作用,管控整个进程内部的其它所有线程
// 2.辅助线程:处理其它函数的线程
//注:调试模式下,如果"断点"主线程,那么其它线程将会暂停运行状态
// 暂停主线程就相当于占用了品目刷新线程,于是屏幕将不会发生变化
int main01(void)
{
HANDLE th = CreateThread(NULL, 0, myFun, NULL, 0, NULL);
system("pause");
SuspendThread(th);//冻结
system("pause");
ResumeThread(th);//解冻
system("pause");
//ExitThread(0);//错误位置
TerminateThread(th, 0);//外部强行退出
//_endthread();
//system("pause");
system("pause");
}
//01.操作线程:线程调度问题
// 开启-->冻结-->解冻-->内部结束-->外部结束
//02.重点内容:冻结&解冻
// 操作单条线程
//03.CreateThead();线程
// 1.DWORD WINAPI&return 0;
// 2.HANDLE hd=CreateThead(NULL,0,fun,NULL,0,NULL);
// NULL:安全等级
// 0:堆栈大小,默认为0就是拷贝自身,每个线程的栈内存都是独立的,而非共享的
// fun:函数指针
// NULL:参数列表
// 0:优先等级
// NULL:线程标识
//04.管理调度线程池:
//05.演示或者操作线程的时候,一般情况之下不要使用IDE进行演示:
// 1.注意线程的优先等级
// 2.关闭线程的特点[主线程与副线程的区别]
// 3.进程依然存在:
// 如果是程序自己结束就会自动结束所有线程并结束该进程,但是这个工具是应用
// 底层操作系统内核驱动来完成的,所以它的操作方法比较高端,于是手动结束的时候
// 线程挂掉,但是进程却没有退出
//06.结束线程的三种方式:
// 内部结束:_endthread();+exitthread();
// 外部结束:terminatethread();
// 全部结束:CloseThreadPool();//关闭线程池
//07.冻结线程与解冻线程:
// SuspendThread();+ResumeThead();
//08.强制结束一条线程:数据容易丢失
// 自我结束语强行结束:return
// 非强制:内部
// _endThread();//指定线程
// ExitThread(0);//默认参数
// 强制:外部
// TerminateThead(hd,0):
// 0代表退出的时候携带一个编号
//09.线程内部的等待采用死循环进行实现
//10.外部操作线程完毕
程序片段(04):01._beginthread.c+02.CreateThead.c
内容概要:线程池
///01._beginthread.c
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
//01.提取创建线程时期由创建函数传递给线程函数的参数:
// 1.读取创建线程传递的参数列表,需要进行类型转换
// 2.空类型的地址(干地址)-->强制类型转换-->获取数据
void run(void * p)
{
char str[10] = { 0 };
sprintf(str, "备胎%d", *(int *)p);
printf("%s \n", str);
}
//02.防止多线程并发访问冲突的方式:
// 1._beginthead(run,0,&a[i]);解释
// (1).多个线程不可以同时访问同一个地址,防止多线程访问冲突
// (2).参数列表说明:
// run:函数指针
// 0:堆栈大小
// &a[i]:参数列表-->这儿的每条线程所访问到的数据都不一样
// 独立访问而非并发访问
// 2._beingthead();函数的返回值是int类型
// 因此_beginthread();和CreateThread();所采取的控制多线程
// 异步操作的方式不一样(一个是int类型的数组,一个是HANDLE类型的数组)
int main01(void)
{
int arr[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
for (int i = 0; i < 10; ++i)
{
int id = _beginthread(run, 0, &arr[i]);
WaitForSingleObject(id, 300);
}
system("pause");
}
///02.CreateThead.c
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
DWORD WINAPI myFun01(void * p)
{
int i = 0;
while (++i)
{
printf("%d \n", i);
Sleep(1000);
}
return 0;
}
int main02(void)
{
HANDLE th = CreateThread(NULL, 0, myFun01, NULL, 0, NULL);
system("pause");
system("pause");
system("pause");
}
DWORD WINAPI myFun02(void * p)
{
char str[10] = { 0 };
sprintf(str, "备胎%d", *(int *)p);
MessageBoxA(0, str, str, 0);
return 0;
}
//01.CreateThread();方式创建线程详解:
// 1.控制多条线程异步执行状态的所需数组:
// HANDLE类型的句柄数组
// 2.创建线程所需的参数说明:
// NULL:安全属性集
// 0:堆栈尺寸,默认拷贝主线程的栈大小,栈独立
// myfun:函数指针
// a+i:参数地址
// 0:优先等级
// NULL:线程编号
int main02(void)
{
int arr[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
HANDLE thArr[10] = { 0 };
for (int i = 0; i < 10; ++i)
{
thArr[i] = CreateThread(NULL, 0, myFun02, thArr + i, 0, NULL);
//WaitForSingleObject(thArr[i], INFINITE);
}
WaitForMultipleObjects(10, thArr, FALSE, INFINITE);
system("pause");
}
程序片段(05):多线程.c
内容概要:操作一条线程
#include
#include
#include
#include
//01.创建一条线程的两种方式:
// 底层方式:CreateThread();
// 表象方式:_beginthread();
//02.结束一条线程的三种方式:
// 内部方式:_endthread();+ExitThread();
// 外部方式:TerminateThread();
//03.线程状态控制的两种方式:
// 冻结:SuspendThread();
// 解冻:ResumeThread();
DWORD WINAPI myFun(void * p)
{
int i = 0;
printf("%d \n", i);
while (++i)
{
printf("%d \n", i);
if (i > 8000)
{
//_endthread();
//ExitThread(0);
}
}
return 0;
}
int main01(void)
{
HANDLE th = CreateThread(NULL, 0, myFun, NULL, 0, NULL);
system("pause");
SuspendThread(th);
system("pause");
ResumeThread(th);
system("pause");
TerminateThread(th, 0);
system("pause");
}
程序片段(06):临界区.c
内容概要:线程临界区解决线程冲突
#include
#include
#include
//01.临界区的使用与操作系统调度线程相关:
// 所有线程的本质调度还是得依赖于操作系统本身
// #include --->临界区结构体
#define N 20
int num = 0;
//02.临界区结构体变量:
// 1.一个用于收钱,一个用于用钱
// 2.定义为全局变量的原因是:
// 为了让多条线程识别同一临界区
CRITICAL_SECTION cs1;
CRITICAL_SECTION cs2;
//03.对临界区的优化操作:
// 减少频繁的进出临界区的次数;
// 有利于提高多线程并发访问的效率
DWORD WINAPI add(void * p)
{
EnterCriticalSection(&cs1);//减少进出
for (int i = 0; i < 10000; ++i)
{
//EnterCriticalSection(&cs1);//频繁进出
++num;
//LeaveCriticalSection(&cs1);//效率低下
}
LeaveCriticalSection(&cs1);//效率提升
return 0;
}
//04.使用临界区的注意事项:
// 1.临界区结构体变量必须是全局变量
// 2.临界区结构体变量最好不要嵌套使用
DWORD WINAPI sub(void * p)
{
EnterCriticalSection(&cs2);
for (int i = 0; i < 10000; ++i)
{
--num;
}
LeaveCriticalSection(&cs2);
return 0;
}
int main(void)
{
//01.初始化临界区结构体变量
InitializeCriticalSection(&cs1);
InitializeCriticalSection(&cs2);
//02.匿名代码块儿的使用方式:
// 划分区域执行代码(复用代码)
//03.要想多线程异步执行状态:
// 需要使用多线程句柄数组
{
HANDLE thArr[N] = { 0 };
for (int i = 0; i < N; ++i)
{
thArr[i] = CreateThread(NULL, 0, add, NULL, 0, NULL);
//WaitForSingleObject(thArr[i], INFINITE);
}
WaitForMultipleObjects(N, thArr, TRUE, INFINITE);
printf("num = %d \n", num);
}
{
HANDLE thArr[N] = { 0 };
for (int i = 0; i < N; ++i)
{
thArr[i] = CreateThread(NULL, 0, sub, NULL, 0, NULL);
}
WaitForMultipleObjects(N, thArr, TRUE, INFINITE);
printf("num = %d \n", num);
}
//04.释放临界区结构体变量
DeleteCriticalSection(&cs1);
DeleteCriticalSection(&cs2);
system("pause");
}
//01.什么是临界区?
// 1.临界区是用于解决多线程并发访问冲突问题的
// 2.包括Cocos2dx当中的C++封装线程,基本上都是
// 我们现在所使用的同步方式(这里的线程操作方式就是实质)
// 3.临界区所支持的最大线程个数为64,Windows操作系统之上
// 线程个数限制,但是服务器操作系统上面没有限制
// 4.临界区的使用步骤:
// 创建-->初始化-->进入-->离开-->删除
//02.线程通信-->进程通信:
// 并发访问错误:收钱特点,收不过来之后
//03.防止多线程并发访问错误:
// 1.WaitForSingleObject(hd[i]);等待单个线程逐个退出-->缺点:效率低下
// 2.避免多线程并发访问问题,并且提高多线程操作效率的方式:
// 临界区-->跑到最近的位置-->逐个给钱[以前的情况是一个一个的处理]
// 临界区&互斥量
//04.临界区的完整使用过程:
// 创建临界区:全局声明
// CRITICAL_SECTION cs;
// 初始化临界区:Main();
// InitializeCriticalSection(&cs);
// 使用临界区:
// 进入:EnterCriticalSection();
// 退出:LeaveCriticalSection();
// 释放临界区:DeleteCriticalSection();
// 1.因为临界区结构体变量内部存在指针情况,会耗费内存,所以需要释放临界区
// 2.还有原因就是因为我们这里申请的是操作系统调度(避免过度占用操作操作系统资源)
//05.线程库的初始化特点:
// C语言不会自动进行初始化,C++会进行自动的初始化动作
//06.解决多线程并发访问冲突的两种方式最大特点:
// 同步方式:
// 1.效率低下
// 2.一次只有一条线程处于执行状态
// 临界方式:
// 1.效率优化
// 2.一次拥有多条线程处于执行状态
//注:临界方式一次有(N-1)条线程处于等待执行状态,但是总有一条是处于执行状态
//注:同步方式和临界方式最大的区别就在于处于等待执行状态的线程数目不同
// 同步方式:没有等待执行状态的线程
// 临界方式:用于等待执行状态的线程
// 相当于一个有预热状态,一个没有预热状态!
程序片段(07):01.事件.c+02.事件.c+03.事件.c
内容概要:线程通信事件机制
///01.事件.c
#include
#include
#include
//01.事件:确定为3个
HANDLE eventArrA[3] = { 0 };
HANDLE threadArrA[3] = { 0 };
//02.同步:两个线程-->三个线程-->多个线程
DWORD WINAPI firstThread(void * p)
{
MessageBoxA(0, "1", "1", 0);
printf("第一个线程执行完毕! \n");
SetEvent(eventArrA[0]);//设置事件(发出Event通知)
return 0;
}
//03.事件用于多条线程之间的通信:
// 可以用于控制多线程的情况之下,各条线程之间的执行顺序
//注:设置事件相当于发出通知,等待事件相当于处理通知
DWORD WINAPI secondThread(void * p)
{
//等待一个事件执行完毕[等待Event信号的出现,再执行下一步操作]
WaitForSingleObject(eventArrA[0], INFINITE);//处理事件(等待Event通知)
MessageBoxA(0, "2", "2", 0);
printf("第二个线程执行完毕! \n");
return 0;
}
//04.句柄类型:
// 创建事件的方式+创建线程的方式
int main01(void)
{
//1.初始化事件参数:
// NULL:安全等级+TRUE:人为设定+FALSE:触发状态+NULL:事件名称
eventArrA[0] = CreateEvent(NULL, TRUE, FALSE, NULL);
eventArrA[1] = CreateEvent(NULL, TRUE, FALSE, NULL);
//2.两条线程并行弹出盒子,现在需要控制线程的执行顺序:
threadArrA[0] = CreateThread(NULL, 0, firstThread, NULL, 0, NULL);
threadArrA[1] = CreateThread(NULL, 0, secondThread, NULL, 0, NULL);
//3.维持多线程异步执行状态:
WaitForMultipleObjects(2, threadArrA, TRUE, INFINITE);
printf("全部等待完成! \n");
system("pause");
}
//01.线程同步的问题:
// 1.服务器当中的多条线程:
// (1).将外部数据写入到本地文件
// (2).从本地文件读取数据到内存
// 2.相关问题:
// (1).没有写入之前无法进行读取
// (2).但是这两条线程是同时开启的
// 3.模型:
// (1).文本-->写入-->读取[配置文件特点]
// (2).先写入本地文件,再进行本地文件读取
// (3).同时开启的时候,就需要进行多线程通信,以确保线程的执行流程
//02.线程通信:事件机制[用于管理线程通信]
// 1.同时启动的两条线程,将会任意确定执行顺序,所以需要线程调度
// 2.实现时间等待的特点(线程通信)
//03.事件用作通知:CreateEvent();事件处理机制
// A:默认的安全设定0
// B:TRUE人工设定&FALSE自动设定
// C:是否进入到触发状态
// D:事件名称
//04.执行流程:聊天儿系统[多事件处理]
// 1.等待执行原理的使用
// 2.创建事件,线程都是在主函数内完成
// (1).事件必须是全局变量(多条线程访问统一标识!)
// (2).线程必须是全局变量(任意函数都可以创建线程)
// (3)SetEvent(e);WaitForSingleObject(e);
// 设置(触发)+等待(处理)<->发出通知+处理通知
//05.聊天儿系统原理:
// 1.多个时间连续
// 2.多层嵌套问题
///02.事件.c
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
HANDLE threadArrB[3] = { 0 };//多线程处理
HANDLE eventArrB[4] = { 0 };//线程通信
//01.全局变量:所有线程使用同一临界区标识
// 用于解决全局的多线程并发访问冲突问题
CRITICAL_SECTION csB;//线程互斥
//02.全局变量:多线程之间的通信内容
// 1.代表聊条儿内容的缓冲区
// 2.memset(str, '\0', 1024);
// 暂时用不到,三方通话机制需要使用
// 3.三方通信原理:中介者设计模式
// 海华和芳芳不会直接接触的特点;
// 都是通过媒婆进行间接接触的特点
char strB[1024] = { 0 };//通信内容
//03.三方通信原理简介:
// 海华-->媒婆(设置事件0)
// 媒婆(处理事件0)
// 媒婆-->芳芳(设置事件1)
// 芳芳(处理事件0)
// 芳芳-->媒婆(设置事件2)
// 媒婆(处理事件2)
// 媒婆-->海华(设置事件3)
// 海华(处理事件3)
DWORD WINAPI haiHuaB(void * p)
{
int i = 1;
EnterCriticalSection(&csB);
memset(strB, '\0', 1024);//防止并发访问冲突
sprintf(strB, "haiHua第%d次说:i love you fangfang! \n", i);//三方通信
LeaveCriticalSection(&csB);
Sleep(1000);//不会占用过多的互斥时间(模拟数据生成时间)
SetEvent(eventArrB[0]);//发出通知
//00:模拟第一次的数据设置(通信主动发出者:主动者)
while (++i)
{
WaitForSingleObject(eventArrB[3], INFINITE);//等待通知(必须等待)
EnterCriticalSection(&csB);
memset(strB, '\0', 1024);//内容清空
sprintf(strB, "haiHua第%d次说:i love you fangfang! \n", i);
LeaveCriticalSection(&csB);
Sleep(1000);//模拟现实
SetEvent(eventArrB[0]);
}
return 0;
}
DWORD WINAPI ruiFuB(void * p)
{
int i = 0;
while (++i)
{
WaitForSingleObject(eventArrB[0], INFINITE);
EnterCriticalSection(&csB);
printf("媒婆给HaiHua传递:%s \n", strB);
LeaveCriticalSection(&csB);
Sleep(1000);
SetEvent(eventArrB[1]);
WaitForSingleObject(eventArrB[2], INFINITE);
EnterCriticalSection(&csB);
printf("媒婆传递FangFang:%s \n", strB);
LeaveCriticalSection(&csB);
Sleep(1000);
SetEvent(eventArrB[3]);
}
//WaitForSingleObject(eventArrB[0], INFINITE);
//printf("%s \n", strB);
//SetEvent(eventArrB[1]);
//WaitForSingleObject(eventArrB[2], INFINITE);
//printf("%s \n", strB);
return 0;
}
DWORD WINAPI fangFangB(void * p)
{
int i = 0;
while (++i)
{
WaitForSingleObject(eventArrB[1], INFINITE);
EnterCriticalSection(&csB);
memset(strB, '\0', 1024);
sprintf(strB, "fangFang第%d次说:i love you haihua! \n", i);
LeaveCriticalSection(&csB);
Sleep(1000);
SetEvent(eventArrB[2]);
}
return 0;
}
int main02(void)
{
InitializeCriticalSection(&csB);
eventArrB[0] = CreateEvent(NULL, TRUE, FALSE, NULL);
eventArrB[1] = CreateEvent(NULL, TRUE, FALSE, NULL);
threadArrB[0] = CreateThread(NULL, 0, haiHuaB, NULL, 0, NULL);
threadArrB[1] = CreateThread(NULL, 0, ruiFuB, NULL, 0, NULL);
threadArrB[2] = CreateThread(NULL, 0, fangFangB, NULL, 0, NULL);
WaitForMultipleObjects(3, threadArrB, TRUE, INFINITE);
printf("over!!! \n");
DeleteCriticalSection(&csB);
system("pause");
}
//01.双方通信的原理:你发一条,我收一条,然后我再发送一条给你,你接收我一条
//02.显示模块儿抽离出来之后,就是一个单独的函数,用于展示内容
// 1.同时做两个检测,三个信号-->防止操作系统死锁
// 2.说话的同时i需要进行通知,逐级进行通知
// 3.原理:说话+通知[Show进行通知]-->三部曲
// 4.大家都在等待Show-->中介者模式&媒婆模式
//03.流程分析:
// haihua发送消息-->媒婆接收消息
// 媒婆收到消息-->媒婆通知芳芳
//04..中介者模式:
// 1.haihua-->发出信号0
// 2.zhongjiezhe-->等待信号0--发出信号1
// 3.wangfang-->等待信号1--发出信号2
// 4.zhongjiezhe-->等待信号2-->发出信号3
// 5.haihua-->等待信号3--发出信号0
//05.事件审批逐级审批原理
// 用于多条线程之间的逐级通信
///03.事件.c
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
//01.HANDLE:操作系统提供的句柄标识,原始类型为(void *)
HANDLE threadArr[3] = { 0 };//线程句柄数组
HANDLE eventArr[4] = { 0 };//事件句柄数组
//02.CRITICAL_SECTION:操作系统内核提供的临界区结构体变量
CRITICAL_SECTION cs = { 0 };//临界区全局变量:整个多线程体系都能够识别的唯一标识
char str[1024] = { 0 };//全局信息公告栏{代表聊天内容的缓冲区}
//03.中介者设计模式原理:
// 海华向媒婆 设置事件0
// 媒婆从海华 等待事件0
// 媒婆向芳芳 设置事件1
// 芳芳从媒婆 等待事件1
// 芳芳向媒婆 设置事件2
// 媒婆从芳芳 等待事件2
// 媒婆向海华 设置事件3
// 海华从媒婆 等待事件3
// 海华向媒婆 设置事件0
DWORD WINAPI haiHua(void *p)//WINAPI:操作系统提供的待调度标识
{
int i = 1;//标识海华主动发言
EnterCriticalSection(&cs);//进入临界区
memset(str, '\0', 1024);//内容清空
sprintf(str, "haiHua第%d次说:i love you Fang! \n", i);//发言内容
LeaveCriticalSection(&cs);//离开临界区
Sleep(1000);//模拟现实通信事件间隔
SetEvent(eventArr[0]);//发出事件通知0
while (++i)
{
WaitForSingleObject(eventArr[3], INFINITE);//等待事件通知3
EnterCriticalSection(&cs);
memset(str, '\0', 1024);
sprintf(str, "\nhaiHua第%d次说:i love you Fang! \n", i);
LeaveCriticalSection(&cs);
Sleep(1000);
SetEvent(eventArr[0]);
}
return 0;
}
DWORD WINAPI ruiFu(void *p)
{//中介者不断进行扫描等待线程通信信息
int i = 0;
int flag = 0;
while (++i)
{
if (!flag)
{//判断通信内容发送方
WaitForSingleObject(eventArr[0], INFINITE);
EnterCriticalSection(&cs);
printf("媒婆传递%d次信息%s \n", i, str);
LeaveCriticalSection(&cs);
Sleep(1000);
SetEvent(eventArr[1]);
flag = 1;//发送方判定标识切换
}
else
{
WaitForSingleObject(eventArr[2], INFINITE);
EnterCriticalSection(&cs);
printf("媒婆传递%d次信息%s \n", i, str);
LeaveCriticalSection(&cs);
Sleep(1000);
SetEvent(eventArr[3]);
flag = 0;
}
}
return 0;
}
DWORD WINAPI fangFang(void *p)
{
int i = 0;
while (++i)
{
WaitForSingleObject(eventArr[1], INFINITE);
EnterCriticalSection(&cs);
memset(str, '\0', 1024);
sprintf(str, "fangFang第%d次说:i love you too! \n", i);
LeaveCriticalSection(&cs);
Sleep(1000);
SetEvent(eventArr[2]);
}
return 0;
}
void main03()
{
//01.初始化临界区结构体变量
InitializeCriticalSection(&cs);
//02.初始化事件句柄数组
eventArr[0] = CreateEvent(NULL, TRUE, FALSE, NULL);
eventArr[1] = CreateEvent(NULL, TRUE, FALSE, NULL);
eventArr[2] = CreateEvent(NULL, TRUE, FALSE, NULL);
eventArr[3] = CreateEvent(NULL, TRUE, FALSE, NULL);
//03.创建并执行多条异步线程
threadArr[0] = CreateThread(NULL, 0, haiHua, NULL, 0, NULL);
threadArr[2] = CreateThread(NULL, 0, ruiFu, NULL, 0, NULL);
threadArr[1] = CreateThread(NULL, 0, fangFang, NULL, 0, NULL);
//04.等待除中介者之外其他的线程执行完毕之后,结束多线程状态
WaitForMultipleObjects(2, threadArr, TRUE, INFINITE);
printf("over");
//05.释放临界区结构体变量:指针&操作系统调度
DeleteCriticalSection(&cs);
system("pause");
}
程序片段(08):线程冲突.c
内容概要:线程互相排斥
#include
#include
#include
//01.全局变量:表示多线程异步并发访问的通信内容
// 极度容易导致多线程并发访问同一内容的冲突性
int num = 0;
//02.互斥量:实质为(void *)类型的空类型地址变量
// 1.同一个互斥量最多能够解决64个线程以内的多线程异步并发访问
// 所导致的冲突问题(这个限制仅仅只是针对于Windows操作系统)
// (针对于服务器之上的多线程异步并发访问线程数没有限制)
// 2.互斥量和临界区的作用相同:
// 同是用于解决多线程异步并发访问所导致的冲突问题!
HANDLE mutex = NULL;
DWORD WINAPI add(void * p)
{
WaitForSingleObject(mutex, INFINITE);//添加互斥状态
if (NULL == mutex)
{//添加互斥出错
printf("添加线程互斥量失败! \n");
abort();
}
for (int i = 0; i < 10000; ++i)
{
++num;
}
ReleaseMutex(mutex);//解除互斥状态
return 0;
}
int main01(void)
{
mutex = CreateMutex(NULL, FALSE, NULL);
HANDLE threadArr[64] = { 0 };
for (int i = 0; i < 64; ++i)
{
threadArr[i] = CreateThread(NULL, 0, add, NULL, 0, NULL);
if (NULL == threadArr[i])
{
printf("线程创建失败! \n");
}
}
WaitForMultipleObjects(64, threadArr, TRUE, INFINITE);
printf("num = %d \n", num);
for (int i = 0; i < 64; ++i)
{
CloseHandle(threadArr[i]);//关闭每条线程所占用的资源
}
CloseHandle(mutex);//关闭互斥量所占用的资源
system("pause");
}
//01.多线程互斥量问题:线程之间互相排斥
// 1.临界区-->互斥量-->设计模式:队列传递强化
// 2.互斥量:和临界区一个作用,用于解决线程冲突问题
//注:临界区解决代码段儿互斥+互斥量解决共享资源互斥访问
//02.互斥量的缺点:
// 1.当我们所需操作的线程个数超过64个的时候就不行了
// 2.所有的线程都有数量限制,你在一定数量之下都处于正常情况,
// 一定数量之上就不会正常了[冲突临界的线程数目限制]
//03.使用互斥量的步骤:
// 声明互斥量:全局变量
// HANDLE mutex = NULL;
// 初始化互斥量:
// mutex = CreateMutex(NULL,FALSE,NULL);
// 使用互斥量:
// 等待:WaitForSingleObject(mutex,INFINITE);//添加互斥状态
// 释放:ReleaseMutex(mutex);//解除互斥状态
// 关闭互斥量:
// CloseHandle(mutex);
程序片段(09):01.Run.c+02.原子变量与原子操作.c
内容概要:原子变量与原子操作
///01.Run.c
#include
#include
#include
//01.关于调试(Debug)和发行(Release)状态特点:
// Debug:不会进行代码自动优化操作
// Release:会进行代码自动优化操作
//02.关于register和volatile两个关键字:
// register:将内存变量移植为寄存器变量(寄存器变量标识)
// volatile:防止内存变量移植为寄存器变量(内存变量标识)
//03.for(int i = 0; i < INT_MAX; ++i){}:
// 当前这段儿代码在编译器Release发行模式之下,将会在
// 内存变量第一次移植到寄存器之后,不在频繁读写内存变量
//原因:一直使用同一内存变量+循环执行体没有操作该内存变量
int main01(void)
{
for (volatile int i = 0; i < INT_MAX; ++i) {}
printf("over");
system("pause");
}
//04.区分编译器的优化与非优化情况:
// 1.早期编译器和近期编译器:
// 早期编译器不会优化,近期编译器会涉及优化
// 2.Debug模式和Release模式:
// Debug模式下会优化(操作寄存器),Release模式不会优化(操作内存)
//注:根据地址访问数据相当于强制读取内存操作(volatile操作)
volatile int num = 20;
DWORD WINAPI rmsg(void * p)
{//多线程读取
//int * px = p;
int * px = (int *)p;
while (1)
{
//printf("%d \n", *px);//当心这儿的副本机制
//int data = *px;//根据地址读取数据,相当于强制读取内存
printf("%d \n", num);
Sleep(1000);
}
}
DWORD WINAPI wmsg(void * p)
{//多线程写入
int * px = (int *)p;
while (1)
{
*px += 1;
Sleep(1000);
}
}
int main01(void)
{
CreateThread(NULL, 0, wmsg, &num, 0, NULL);
CreateThread(NULL, 0, wmsg, &num, 0, NULL);
CreateThread(NULL, 0, wmsg, &num, 0, NULL);
CreateThread(NULL, 0, rmsg, &num, 0, NULL);
system("pause");
}
//01.原子变量主要用于解决什么样儿的问题?
// 还是一样的问题,多线程异步并发访问冲突问题(效率最高)
//02.三方互相通信-->技术含量最高-->会使用到所有的多线程知识
// 只是:临界区,互斥量,原子变量等等...
//03.原子变量:Atomitic
// 不可拆解的变量-->变量修改问题:两个变量同时访问一个
// 第三方变量,造成最终的访问结果不正确(因此需要限定原子访问)
//04.volatile:
// Debug模式:
// Release模式:代码优化作用,主要解决代码更快的效果
// 代码优化:让你的代码更快,发现该变量在寄存器当中
// 没有相应的操作,于是就直接读取内存当中的数据
//05.Realese:实现无线等待的方式
//06.操作系统操作所有的变量都是依赖于寄存器的:
// 寄存器读取副本-->强制读取寄存器当中的数据
// 寄存器当中的原始副本-->与内存当中的普通副本
// 寄存器原始与副本[区分寄存器副本与内存副本]
//07.当指针指向一个变量之后,操作指针就等同于操作原本
// 注意副本机制
//08.不同编译器的演示使用
// 多线程现代编译器的优化特性
//09.寄存器变量与内存变量:
// register + volatile
//注:强制读写内存的情况之下等同于寄存器和内存状态实时对应!
///02.原子变量与原子操作.c
#include
#include
#include
//01.全局变量:用于表示多线程异步并发访问之间的通信信息
// 1.对于全局变量的访问分为两种(一种是写入,一种是读取):
// 针对于写入操作,会发生多线程异步并发访问冲突问题
// 针对于读取操作,不会发生多线程异步并发访问冲突 问题
// 2.解决多线程异步执行并发访问操作的冲突问题:
// 临界区:用于代码段儿
// 互斥量:用于共享变量
// 原子量:用于单独变量
//注:事件通知是用于线程之间的通信,例如让多条线程按照指定
// 的顺序进行代码段儿的执行
//注:原子操作的速度要快于互斥量和临界区的操作
// 1.因此,一般情况之下,如果多个线程同时访问一个变量,就建议
// 采用原子变量
// 2.简单的原理:
// int类型的变量操作-->其它类型的操作-->线程安全问题
int num = 0;
//02.对于当前这个线程任务代码函数的剖析:
// 1.这儿的++num翻译为汇编语言,以便于看清实质
// 2.翻译为汇编语言之后,就不只是一条执行语句了:
// (1).先将该内存变量载入进寄存器当中,形成寄存器变量
// (2).接着调用完成执行++num;操作
// (3).再将运算完成之后的结果返回到内存当中
// 3.因此++num;这条代码真实的运行操作其实是三条语句
// 体现了非原子性,因此容易出现多线程异步并发访问问题
// 4.由此,退出了原子变量的概念,相当于将这三个操作打包为
// 一个整体操作,多线程情况之下的同一时刻,只能由一条线
// 程进行访问操作!
// 5.C和CPP包括游戏里面都提供了这种原子变量的操作机制:
// 原子变量的操作方法
//03.原子操作的方式分类:
// 1.分"类型"进行处理
// 2.分"写入"和"读取"进行处理
// 3.分"增加"和"减少"进行处理
// 4.分"原本"和"副本"进行分类
//注:这里边需要一个volatile类型的指针,用于强制读写内存
DWORD WINAPI runX(void * p)
{
for (int i = 0; i < 10000; ++i)
{
//++num;
//InterlockedIncrement(&num);//(++num) or (num++)
//InterlockedIncrement(&num, 1);//可指明递增数
//InterlockedExchange(&num);//可修改性
//InterlocakedAdd(&num);//改变的是副本,原本不会发生改变
InterlocakedExchangeAdd(&num, 1);//原本会发生改变
}
}
int main01(void)
{
HANDLE threadArr[50] = { 0 };
for (int i = 0; i < 50; ++i)
{
threadArr[i] = CreateThread(NULL, 0, runX, NULL, 0, NULL);
}
WaitForMultipleObjects(50, threadArr, TRUE, INFINITE);
printf("num = %d \n", num);
system("pause");
}
//01.原子变量与原子操作(C++概念)
// 同样用于解决线程安全问题
//02.原子变量主要解决什么样的问题?
// 多个线程并发访问一个全局变量常常都会发生一定的问题
// 所以需要进行解决(原子操作:不可分割的操作!)
//03.C语言当中的0和NULL在数值方面是完全等价的
// 只是一个具备地址层面的意义,一个不具有
//04.为了精确控制全局变量的多线程并发访问问题:
// 需要进行控制访问特性
//5.什么是线程安全?
// 纯C编写的-->C++编写-->多线程必须
程序片段(10):main.c
内容概要:volatile
#include
#include
#include
#include
int num = 1120;//现代编译器:自动优化内存变量为寄存器变量
DWORD WINAPI rmsg(void *p)
{//读取
int * px = (int *)p;
while (1)
{
//int data = *px;//等同于强制读内存
printf("\n%d", num);
Sleep(1000);
}
}
DWORD WINAPI wmsg(void *p)
{//写入
int * px = (int*)p;
while (1)
{
*px += 1;
Sleep(10000);
}
}
void main01()
{
CreateThread(NULL, 0, msg, &num, 0, NULL);
CreateThread(NULL, 0, msg, &num, 0, NULL);
CreateThread(NULL, 0, msg, &num, 0, NULL);
CreateThread(NULL, 0, cmsg, &num, 0, NULL);
system("pause");
}
程序片段(11):Time.c
内容概要:时间同步
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
//01.时间同步:定时器操作
// 1.由于操作系统自带时间定时器,因此不用为程序独立创建时间定时器:
// 利用已存在的资源,避免过多的占用资源
// 2.定时器的实质还是一种事件通知机制:
// 在定时器到达了某一个指定刻度之后,发出某个事件通知
//注:需要检测定时器的创建成功和失败情况!
//02.定时器编程特点剖析:
// 1.定时器的精准性,精确到小数点儿之后的第7位
// 2.由于一个联合体,因此能够进行时间精准度的设定
//注:timer描述时间刻度非常精准
int main01(void)
{
HANDLE timer = CreateWaitableTimer(NULL, TRUE, NULL);
if (NULL == timer)
{
printf("定时器创建失败! \n");
abort();
}
LARGE_INTEGER time;
//-5000 000 0 :毫秒-->微妙-->0.1微妙
time.QuadPart = -20000000;//延迟2秒启动
//10^-7方秒-->等待功能
//设置定时器等待两秒
SetWaitableTimer(timer, &time, 0, NULL, 0, NULL);//设置时间
//接收一个消息
if (WAIT_OBJECT_0 == WaitForSingleObject(timer, INFINITE))
{
printf("等待成功! \n");
}
else
{
printf("等待失败! \n");
}
while (1)
{
//01.单线程每隔两秒做一件事情,没机会做其他事情
// 这个叫做线程占用
//02.通过操作系统进行线程的管理,时间进行指定
// 1.操作系统自动管理线程,通过时间进行检测
// 2.时间同步是操作系统进行的管理
// 操作系统原理:每打开一个窗口都一个地址
// (1)操作系统管理原理是通过链表进行管理
// (2)操作系统内部有一个死循环,不断的进行检测
// while(1)
// {读到:键盘,鼠标操作的信息}
// 扫描信息-->消息传给窗口
// 操作-->消息[线程管理]-->本质操作
//03.时间统一的功能都交给了操作系统管理
// 所以我们这里使用操作系统当中的时间同步原理
printf("fangfang \n");
Sleep(2000);
}
system("pause");
}
HANDLE timer;//(1.声明定时器)
DWORD WINAPI go1(void *p)
{
MessageBoxA(0, "1", "1", 0);
//让go1执行完毕之后5秒再执行go2
//(2.创建定时器)
timer = CreateWaitableTimer(NULL, TRUE, NULL);
LARGE_INTEGER time;
time.QuadPart = -50000000;//5秒
//10 -7秒 0.1微秒
SetWaitableTimer(timer, &time, 0, NULL, 0, NULL);//设置定时器等待 2聊
}
DWORD WINAPI go2(void *p)
{
//if (WaitForSingleObject(timer,INFINITE)==WAIT_OBJECT_0)
//{
WaitForSingleObject(timer, INFINITE);
MessageBoxA(0, "2", "2", 0);
printf("等待成功!");
//}
//else
//{
//printf("等待失败!");
//}
}
void main02()
{
//通过主线程进行控制时间同步
HANDLE hd=CreateThread(NULL, 0, go1, NULL, 0, NULL);
//弊端:时间同步不能用于跨线程的同步
//单独定时器:只能用于同步的通信,不能用于异步通信
WaitForSingleObject(hd, INFINITE);
if (WaitForSingleObject(timer,INFINITE)==WAIT_OBJECT_0)
{
CreateThread(NULL, 0, go2, NULL, 0, NULL);
printf("等待成功!");
}
else
{
printf("等待失败!");
}
getchar();
}
//01.根据时间进行同步:
// 原理:确定的时间赶往同一个地点
//02.时间同步:定时器原理[空间原理]
// 定时的完成一件事情
//03.定时器原理:
// 1.主线程独立执行
// 2.时间线程:每隔3秒提供一个函数指针
// 3.接收到事情的时候就开始干活儿
//04.时间锁定
//05.时间同步可能只能执行于一个线程内部
程序片段(12):time.c
内容概要:多线程实战
#include
#include
#include
#include
int i = 1;//全局变量:用于多线程通信
void setTime(void * p)
{//定时器函数:
while (1)
{
Sleep(1000);
char str[40] = { 0 };
sprintf(str, "title 当前时间为第%3d的秒! \n", i++);
system(str);
}
}
void run(void * p)
{//主控制函数
while (1)
{
if (3 == i)
{
system("calc");
}
else if (10 == i)
{
system("notepad");
_endthread();
}
else if (19 == i)
{
system("tasklist & pause");
}
Sleep(1000);
}
}
int main01(void)
{
system("title China World! \n");
_beginthread(setTime, 0, NULL);//开启定时器
_beginthread(run, 0, NULL);//主控制函数
system("pause");
}
程序片段(13):main.c
内容概要:多线程检索数组
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
//01.采用多线程处理:
// 在数组当中只存在一个待查找数据的情况
int isFind = 0;//全局变量-->多线程通信内容
int * pFind = NULL;//查找到的数据位置
typedef struct
{
int arrLen;
int findNum;
int thID;
int * intArr;
} thTaskInfo;
void searchNum(void * p)
{
thTaskInfo * pThTask = (thTaskInfo *)p;
printf("编号为%d的线程开始检索: \n", pThTask->thID);
for (int * pTmp = pThTask->intArr; pTmp < pThTask->intArr + pThTask->arrLen; ++pTmp)
{
if (1 == isFind)
{
printf("编号为%d的线程结束检索状态,其它线程已检索到指定数据! \n", pThTask->thID);
_endthread();
}
if (pThTask->findNum == *pTmp)
{
isFind = 1;
pFind = pTmp;
printf("编号为%d的线程已检索到指定数据%d,该数据的位置为:%p ! \n", pThTask->thID, *pTmp, pTmp);
_endthread();
}
}
printf("编号为%d的线程未检索到指定数据,结束检索状态! \n", pThTask->thID);
}
int main01(void)
{//理想情况之下
int data[1000] = { 0 };
for (int i = 999; i > -1; --i)
{
data[i] = i;
}
thTaskInfo thTaskArr[10] = { 0 };
for (int i = 0; i < 10; ++i)
{
thTaskArr[i].intArr = data + i * 100;
thTaskArr[i].arrLen = 100;
thTaskArr[i].findNum = 767;
thTaskArr[i].thID = i;
_beginthread(searchNum, 0, &thTaskArr[i]);
}
system("pause");
}
//02.多线程检索无规律数据的规则:
// 1.采用N条线程执行检索任务
// 2.其中(N-1)条线程所检索的数据量一致
// 最后1条检索的数据量不一致
// 3.其中(N-1)条线程所检索的数据量单元
// 要大于最后一条线程所检索的数据量单元
int main02(void)
{
int data[1000] = { 0 };
for (int i = 999; i > -1; --i)
{
data[i] = i;
}
int findNum = 0;
int thNum = 0;
scanf("%d %d", &findNum, &thNum);
int quotient = 1000 / thNum;
int remainder = 1000 % thNum;
thTaskInfo * pThTaskArr = (thTaskInfo *)malloc(thNum *sizeof(thTaskInfo));
if (!remainder)
{
for (int i = 0; i < thNum; ++i)
{
(pThTaskArr + i)->intArr = data + i * (1000 / thNum);
(pThTaskArr + i)->arrLen = 1000 / thNum;
(pThTaskArr + i)->findNum = findNum;
(pThTaskArr + i)->thID = i;
_beginthread(searchNum, 0, pThTaskArr + i);
}
}
else
{
for (int i = 0; i < thNum - 1; ++i)
{
(pThTaskArr + i)->intArr = data + i * (1000 / quotient);
(pThTaskArr + i)->arrLen = 1000 / quotient;
(pThTaskArr + i)->findNum = findNum;
(pThTaskArr + i)->thID = i;
_beginthread(searchNum, 0, pThTaskArr + i);
}
int endI = thNum - 1;
(pThTaskArr + endI)->intArr = data + quotient * (thNum - 1);
(pThTaskArr + endI)->arrLen = remainder;
(pThTaskArr + endI)->findNum = findNum;
(pThTaskArr + endI)->thID = endI;
_beginthread(searchNum, 0, pThTaskArr + endI);
}
system("pause");
}
程序片段(14):DualCircle.h+DualCircle.c
内容概要:双环
///DualCircle.h
#pragma once
typedef struct node
{
int data;
struct node * pPre;
struct node * pNext;
}Node;
typedef struct dualcircle
{
Node * pHead;
Node * pTail;
}DualCircle;
void initNodeWithData(Node * pNode, int data);
void initDualCircle(DualCircle * pDualCircle);
void dualCircleHeadInsert(DualCircle * pDualCircle, int data);
void dualCircleTailInsert(DualCircle * pDualCircle, int data);
Node * dualCircleSelectFirst(DualCircle * pDualCircle, int data);
void dualCircleRandInsert(DualCircle * pDualCircle, int findData, int insertData);
void showDualCircle(DualCircle * pDualCircle);
void dualCircleDeleteFirst(DualCircle * pDualCircle, int data);
void dualCircleUpdateFirst(DualCircle * pDualCircle, int oldData, int newData);
///DualCircle.c
#include "DualCircle.h"
#include <Windows.h>
void initNodeWithData(Node * pNode, int data)
{
if (NULL == pNode)
abort();
pNode->data = data;
pNode->pPre = NULL;
pNode->pNext = NULL;
}
void initDualCircle(DualCircle * pDualCircle)
{
if (NULL == pDualCircle)
abort();
pDualCircle->pHead = pDualCircle->pTail = NULL;
}
void dualCircleHeadInsert(DualCircle * pDualCircle, int data)
{
if (NULL == pDualCircle)
abort();
Node * pNew = (Node *)malloc(sizeof(Node));
initNodeWithData(pNew, data);
if (NULL == pDualCircle->pHead)
{//空双环
pNew->pPre = pNew->pNext = pNew;
pDualCircle->pHead = pDualCircle->pTail = pNew;
return;
}
if (pDualCircle->pHead == pDualCircle->pTail)
{//单节点
pNew->pPre = pNew->pNext = pDualCircle->pHead;
pDualCircle->pTail->pPre = pDualCircle->pTail->pNext = pNew;
pDualCircle->pHead = pNew;
return;
}//多节点
pNew->pPre = pDualCircle->pTail;
pNew->pNext = pDualCircle->pHead;
pDualCircle->pHead = pNew;
pDualCircle->pTail->pNext = pNew;
}
void dualCircleTailInsert(DualCircle * pDualCircle, int data){}
Node * dualCircleSelectFirst(DualCircle * pDualCircle, int data)
{
if (NULL == pDualCircle)
abort();
if (NULL == pDualCircle->pHead)
abort();
Node * pTmp = pDualCircle->pHead;
do
{
if (data == pTmp->data)
return pTmp;
pTmp = pTmp->pNext;
} while (pDualCircle->pHead != pTmp);
return NULL;
}
void dualCircleRandInsert(DualCircle * pDualCircle, int findData, int insertData)
{
if (NULL == pDualCircle)
abort();
if (NULL == pDualCircle->pHead)
abort();
int find = 0;
Node * pTmp = pDualCircle->pHead;
do
{
if (findData == pTmp->data)
{
find = 1;
break;
}
pTmp = pTmp->pNext;
} while (pDualCircle->pHead != pTmp);
if (!find)
return;
Node * pInsert = (Node *)malloc(sizeof(Node));
initNodeWithData(pInsert, insertData);
if (pDualCircle->pTail == pTmp)
{
pDualCircle->pTail->pNext = pInsert;
pInsert->pPre = pDualCircle->pTail;
pInsert->pNext = pDualCircle->pHead;
pDualCircle->pHead->pPre = pInsert;
pDualCircle->pTail = pInsert;
return;
}
pInsert->pPre = pTmp;
pInsert->pNext = pTmp->pNext;
pTmp->pNext = pInsert;
pTmp->pNext->pPre = pInsert;
}
//其它的没有写头,自己去想吧!