本博客内容:
一、线程创建函数CreateThread()
二、等待线程返回函数
三、windows下另一个线程函数_beginThreadex()
四、多线程编程之windows同步方式
五、Linux五种IO模型
六、同步、异步、阻塞、非阻塞
七、并发与并行的理解
八、select/poll/epoll的区别
一、线程创建函数CreateThread()
参考:https://www.cnblogs.com/ay-a/p/8762951.html
头文件:#include<windows.h>
原型:
HANDLE WINAPI CreateThread()
参数6个: 默认值 代表含义
安全性 NULL 线程安全,不被继承
栈空间 0 默认分配1M
线程函数 函数
传给线程的参数 无参数使用0 假如要传for循环中的参量i,填写&i即可
是否立即调度 0 创建后立即调度
返回线程的ID号 0 不需要返回ID号
创建成功返回新线程的句柄,失败返回NULL
举例:最简单的使用
#include<windows.h>
#include<stdio.h>
DWORD WINAPI ThreadFunc(LPVOID);
void main()
{
HANDLE hThread;
DWORD threadId;
hThread=CreateThread(NULL,0,ThreadFunc,0,0,&threadId);
printf("我是主线程,pid=%d\n",GetCurrentThreadId());
Sleep(100);
}
DWORD WINAPI ThreadFunc(LPVOID)
{
Sleep(100);
printf("我是子线程,pid=%d\n",GetCurrentThreadId());
return 0;
}
//运行结果很随机
二、等待线程返回函数
多线程编程后,我们需要等待某一线程完成了特定操作后再继续做其他事情,要实现该目的,可使用windows函数WaitForSingleObject,或者WaitForMultiObjects。这2个函数都会等待object被标为有信号(signaled)时才返回。只要是Windows创建的object都会赋予一个状态量。如果被Object被激活了,或正在使用,那么该Object就是无信号,不可用的。
第一个函数:等待单个线程返回
函数原型:
DWORD WINAPI WaitForSingleObject()
参数2个: (只要是句柄对象都可以,包括event,semaphore、thread、mutex)
句柄
等待毫秒 0: 不等待 INFINITE 无限等待
返回值:
WAIT_OBJECT_0 指定的对象有信号状态
WAIT_TIMEOUT 等待超时
举例:
#include<Windows.h>
#include<stdio.h>
DWORD WINAPI ThreadFunc(LPVOID);
int main()
{
HANDLE hThread;
DWORD threadId;
hThread=CreateThread(NULL,0,ThreadFunc,0,0,&threadId);
printf("我是主线程, pid = %d\n", GetCurrentThreadId()); //输出主线程pid
WaitForSingleObject(hThread,0); //不等待,直接返回
return 0;
}
DWORD WINAPI ThreadFunc(LPVOID)
{
Sleep(200);
printf("我是子线程,pid=%d\n",GetCurrentThreadId());
return 0;
}
第二个函数:等待多个线程返回
函数原型: DWORD WINAPI WaitForMultiObjects()
4个参数:
等待个数 0到64中的一个值
句柄数组指针 存放被等待的内核对象的句柄的数组
bool 是否等到所有内核对象为已通知状态后才返回,如果为true,则是这样,如果为false,只要有一个对象为已通知状态时就可以返回。
等待时间
举例:
#include <stdio.h>
#include <windows.h>
const int THREAD_NUM = 10;
DWORD WINAPI ThreadFunc(LPVOID);
int main()
{
printf("我是主线程, pid = %d\n", GetCurrentThreadId()); //输出主线程pid
HANDLE hThread[THREAD_NUM];
for(int i=0;i<THREAD_NUM;i++)
{
hThread[i]=CreateThread(NULL,0,ThreadFunc,&i,0,NULL);//创建线程
}
WaitForMultipleObjects(THREAD_NUM,hThread,false,INFINITE); //只要有一个线程返回就结束 //如果为true,则必须要将所有的都返回才结束
return 0;
}
DWORD WINAPI ThreadFunc(LPVOID p)
{
int n=*(int*)p;
Sleep(1000*n); //第n个线程睡眠n秒
printf("我是, pid = %d 的子线程\n", GetCurrentThreadId()); //输出子线程pid
printf(" pid = %d 的子线程退出\n", GetCurrentThreadId());
return 0;
}
三、windows下另一个线程函数_beginThreadex()
也是windows提供的AP头文件:process.h
尽量多用这个函数原因:
要从标准C运行库与多线程的矛盾说起,标准C运行库在1970年就实现了,当时没任何一个操作系统提供对多线程的支持,因此编写标准C运行库的程序员根本没考虑多线程程序使用标准C运行库的情况,比如标准C运行库的全局变量errno,很多运行库中的函数会在出错时会将错误代码赋值给这个全局变量,这样可以方便调试,如果有这个代码片段:
if(system("notepad.exe readme.txt")==-1)
{
switch(errno)
...//错误处理代码
}
假设某个线程A在执行上面的代码,该线程在调用system()之后尚未调用switch()语句时另外一个线程B启动了,这个线程B也调用了标准C运行库的函数,将错误代号写入了errno中。这样线程A一旦开始执行switch()语句时,它将访问一个被B线程改动了的errno,这种情况需要避免,因为不单单是这个变量会出问题,其他像sterror()、asctime()等函数也会遇到这种由多个线程访问修改导致的数据覆盖问题。
为了解决该问题,windows操作系统提供了这样的解决方案-每个线程都将拥有自己的一块内存区域来供标准C运行库中所有有需要的函数使用。
_beginthreadex()函数在创建新线程时会分配并初始化一个_tiddata块。这个_tiddata块自然是用来存放一些需要线程独享的数据。新线程运行时会首先将_tiddata块与自己进一步关联起来。然后新线程调用标准C运行库函数如strtok()时就会先取得_tiddata块的地址再将需要保护的数据存入_tiddata块中。这样每个线程就只会访问和修改