windows创建线程、IO模型、同步异步

本文介绍了Windows线程创建的函数CreateThread和_beginThreadex,详细讲解了线程同步的不同方式,包括临界区、互斥量、信号量和事件对象。同时探讨了Linux的五种IO模型:阻塞、非阻塞、I/O复用、信号驱动和异步IO。最后,文章对比了同步与异步、阻塞与非阻塞的概念,并分析了并发与并行的区别以及select、poll和epoll在高并发场景下的适用性。
摘要由CSDN通过智能技术生成

本博客内容:
一、线程创建函数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个参数:
等待个数       064中的一个值
句柄数组指针   存放被等待的内核对象的句柄的数组
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块中。这样每个线程就只会访问和修改

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值