多线程CreateThread函数的用法

多线程CreateThread函数的用法CreateThread当使用CreateProcess调用时,系统将创建一个进程和一个主线程。CreateThread将在主线程的基础上创建一个新线程,大致做如下步骤:1在内核对象中分配一个线程标

大家好,又见面了,我是你们的朋友全栈君。

 

CreateThread
当使用CreateProcess调用时,系统将创建一个进程和一个主线程。CreateThread将在主线程的基础上创建一个新线程,大致做如下步骤:
  1在内核对象中分配一个线程标识/句柄,可供管理,由CreateThread返回
  2把线程退出码置为STILL_ACTIVE,把线程挂起计数置1
  3分配context结构
  4分配两页的物理存储以准备栈,保护页设置为PAGE_READWRITE,第2页设为PAGE_GUARD
  5lpStartAddr和lpvThread值被放在栈顶,使它们成为传送给StartOfThread的参数
  6把context结构的栈指针指向栈顶(第5步)指令指针指向startOfThread函数
HANDLE WINAPI CreateThread(
    LPSECURITY_ATTRIBUTES   lpThreadAttributes, //线程安全相关的属性,常置为NULL
    SIZE_T                  dwStackSize,        //新线程的初始化栈在大小,可设置为0
    LPTHREAD_START_ROUTINE  lpStartAddress,     //被线程执行的回调函数,也称为线程函数
    LPVOID                  lpParameter,        //传入线程函数的参数,不需传递参数时为NULL
    DWORD                   dwCreationFlags,    //控制线程创建的标志
    LPDWORD                 lpThreadId          //传出参数,用于获得线程ID,如果为NULL则不返回线程ID
);

第一个参数是指向SECURITY_ATTRIBUTES型态的结构的指针。在Windows 98中忽略该参数。在Windows NT中,它被设为NULL。
第二个参数是用于新线程的初始堆栈大小,默认值为0。在任何情况下,Windows根据需要动态延长堆栈的大小。
第三个参数是指向线程函数的指标。函数名称没有限制,但是必须以下列形式声明:
DWORD WINAPI ThreadProc (PVOID pParam) ;
第四个参数为传递给ThreadProc的参数。这样主线程和从属线程就可以共享数据
第五个参数通常为0,但当建立的线程不马上执行时为旗标CREATE_SUSPENDED。线程将暂停直到呼叫ResumeThread来恢复线程的执行为止。表示创建线程的运行状态,其中CREATE_SUSPEND表示挂起当前创建的线程,而0表示立即执行当前创建的进程;
第六个参数 lpThreadID:返回新创建的线程的ID编号;是一个指标,指向接受执行绪ID值的变量。 

如果函数调用成功,则返回新线程的句柄,调用WaitForSingleObject函数等待所创建线程的运行结束。函数的格式如下:

参数的含义如下:
hHandle:指定对象或时间的句柄;

dwMilliseconds:等待时间,以毫秒为单位,当超过等待时间时,此函数返回。如果参数设置为0,则该函数立即返回;如果设置成INFINITE,则该函数直到有信号才返回。

一般情况下需要创建多个线程来提高程序的执行效率,但是多个线程同时运行的时候可能调用线程函数,在多个线程同时对一个内存地址进行写入操作,由于CPU时间调度的问题,写入的数据会被多次覆盖,所以要使线程同步。

就是说,当有一个线程对文件进行操作时,其它线程只能等待。可以通过临界区对象实现线程同步。临界区对象是定义在数据段中的一个CRITICAL_SECTION结构,Windows内部使用这个结构记录一些信息,确保同一时间只有一个线程访问改数据段中的数据。

使用临界区的步骤如下:

(1)初始化一个CRITICAL_SECTION结构;在使用临界区对象之前,需要定义全局CRITICAL_SECTION变量,在调用CreateThread函数前调用InitializeCriticalSection函数初始化临界区对象;

(2)申请进入一个临界区;在线程函数中要对保护的数据进行操作前,可以通过调用EnterCriticalSection函数申请进入临界区。由于同一时间内只能有一个线程进入临界区,所以在申请的时候如果有一个线程已经进入临界区,则该函数就会一直等到那个线程执行完临界区代码;

(3)离开临界区;当执行完临界区代码后,需要调用LeaveCriticalSection函数离开临界区;

(4)删除临界区;当不需要临界区时调用DeleteCriticalSection函数将临界区对象删除;

下面的代码创建了5个线程,每个线程在文件中写入10000个“hello”:

 

 

DWORD WaitForSingleObject(
                          HANDLE hHandle,
                          DWORD dwMilliseconds
                         );

 

注意:临界区要在线程执行前初始化,因为线程一但被建立即开始运行(除非手工挂起),但线程建立后在初始化临界区可能出现问题

#include <stdlib.h>
#include <iostream>
#include <list>
#include <conio.h>
#include <time.h>
#include <algorithm>
#include <windows.h>

using namespace std;
 
//volatile是一个类型修饰符,表示这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了
volatile int b = 0;
//int b = 0;
 
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
    int i = 10000;
    int *p = (int*)lpParameter;
    while(i--)
    {
        (*p)++;
        b++;
    }
 
    return 0;
}
 
int main(int argc, char* argv[])
{
    int a = 0;
 
    HANDLE hThread1 = CreateThread(NULL, 0, ThreadProc, &a, 0, NULL);
    HANDLE hThread2 = CreateThread(NULL, 0, ThreadProc, &a, 0, NULL);
    HANDLE hThread3 = CreateThread(NULL, 0, ThreadProc, &a, 0, NULL);
    HANDLE hThread4 = CreateThread(NULL, 0, ThreadProc, &a, 0, NULL);
    HANDLE hThread5 = CreateThread(NULL, 0, ThreadProc, &a, 0, NULL);
 
    Sleep(1000);
 
    CloseHandle(hThread1);
    CloseHandle(hThread2);
    CloseHandle(hThread3);
    CloseHandle(hThread4);
    CloseHandle(hThread5);
 
    cout << "a = " << a << endl;
    cout << "b = " << b << endl;
 
    system("pause");
    return 0;
}

每次运行 得到的结果有可能不一致

a = 43955
b = 42426

//

a = 50000
b = 50000

 

 回调函数必须是静态成员函数或全局函数  ,不放在类里,不必static,

放在类里一定要static

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/154948.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • nvcat premium 15 激活码_在线激活

    (nvcat premium 15 激活码)JetBrains旗下有多款编译器工具(如:IntelliJ、WebStorm、PyCharm等)在各编程领域几乎都占据了垄断地位。建立在开源IntelliJ平台之上,过去15年以来,JetBrains一直在不断发展和完善这个平台。这个平台可以针对您的开发工作流进行微调并且能够提供…

    2022年3月27日
    109
  • mysql错误代码1142_mysqldump命令

    mysql错误代码1142_mysqldump命令I’mhavingtroubleswithacertainqueryononeofmyservers.OnallotherplacesI’vetestedititworkscompletelyfinebutontheserveriwanttouseititisn’tworking.It’saboutthefollowing…

    2022年10月1日
    0
  • 微信开放平台认证后怎么实现扫码登录功能[通俗易懂]

    微信开放平台认证后怎么实现扫码登录功能[通俗易懂]主题:微信开放平台认证后怎么实现扫码登录功能在面对这些问题时,同时也是很多刚入手做开发朋友疑惑的希望能帮你们快速完成微信登录配置。以我多天的日夜辛苦测试和实践,同时也是我自己在开发过程中遇到的问题被我不断尝试,一遍又一遍的填写参数,建站平台换几十个,最总发现了这些问题所在给大家总结了以下几点问题和解决方案:1.认证微信开放平台和微信公众平台认证有区别吗?如果只用来作微信登录和使用微信支付,那么告诉你:没有区别,同样是三百块钱认证费用(你只注意一下你的开发资料能一次性通过就行)2.同样能实

    2022年6月9日
    75
  • 网络配置——Linux运维基础

    网络配置——Linux运维基础

    2021年12月3日
    35
  • intellij idea运行配置_idea2017配置jdk

    intellij idea运行配置_idea2017配置jdkIntelliJidea2017.2配置Tomcat8.5前期准备IDEA、JDK、Tomcat什么的先装好,环境配置好,本文中没有这些配置博客图片为主请注意看仔细第一步当然先得建一个web项目1、file-&gt;new-&gt;project-Next-&gt;Finish-项目建好了接下来就是配置了-工具栏点击上图图标或【F4】或项目右键【OpenModuleS…

    2022年8月31日
    6
  • flex垂直居中[通俗易懂]

    flex垂直居中[通俗易懂]{display:flex;justify-content:center;align-items:center;}以上代码可以使元素自动水平垂直居中{flex:1;}以上代码可以使子元素都有相同的长度,且忽略它们内部的内容:flex容器属性1、触发弹性盒:display:flex、inline-flex  注意,设为Flex布局以后,子元素的float、clear和vertical-align属性将失效。2、flex-directio.

    2022年4月26日
    79

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号