多线程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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 搭建Socks5代理服务器[通俗易懂]

    搭建Socks5代理服务器[通俗易懂]搭建Socks5代理服务器本篇介绍在centos7上搭建一个socks5的代理服务器。1、安装依赖软件包[root@sg~]#yuminstallgccgcc-c++pam-developenldap-developenssl-devel-y2、下载源码、解压、编译、安装[root@sg~]#wget–no-check-certificatehttps://jaist.dl.sourceforge.net/project/ss5/ss5/3.8.9-8/ss5-3.8

    2022年6月19日
    327
  • 淘宝装修代码大全(完整版)

    淘宝装修代码大全(完整版)1.公告栏的装修图片代码,帖到公告栏就行了.不过还是有好多人来问我公告栏的位置—–点我的淘宝—–管理我的店铺——基本设置,下面写着公告的位置。<imgsrc=&quot

    2022年7月4日
    27
  • 线程的IsBackground属性「建议收藏」

    线程的IsBackground属性「建议收藏」.Net的公用语言运行时(CommonLanguageRuntime,CLR)能区分两种不同类型的线程:前台线程和后台线程。这两者的区别就是:应用程序必须运行完所有的前台线程才可以退出;而对于后台线程,应用程序则可以不考虑其是否已经运行完毕而直接退出,所有的后台线程在应用程序退出时都会自动结束。.net环境使用Thread建立的线程默认情况下是前台线程,即线程属性IsBackground=

    2022年10月17日
    2
  • webpack版本选择_webpack官网

    webpack版本选择_webpack官网1.先确认node和npm有没有安装node-vnpm-v2.创建一个打包文件夹mkdirtest3.全局安装webpack和webpack-cli使用npm安装会卡在一个地方,因此这里我使用cnpm,没有安装的话,可以百度看看怎么安装cnpminstallwebpack@3.39.2i-gcnpminstallwebpack-cli@3.3.7-gcnpminstallwebpack-dev-server@3.8.0-g4.在test文件夹下面安装对应版

    2022年8月10日
    7
  • 软件工厂简介「建议收藏」

    软件工厂简介「建议收藏」摘要:简要介绍Microsoft开发软件工厂这种方法的动机。所谓软件工厂就是指为了支持某种特定应用程序的快速开发而配置的开发环境。软件工厂从逻辑上讲就是软件开发方法和实践的下一个发展阶段。然而,通过引入产业化模式,软件工厂势必会改变软件行业的现状。扩大软件开发的规模从目前的情况来看,软件开发的速度缓慢、代价高昂而又极易出错,常常会生产出存在大量缺陷的产品,在可用性、可靠性、性能、安全

    2025年8月26日
    6
  • Apache配置虚拟主机_apache启动但是访问不到

    Apache配置虚拟主机_apache启动但是访问不到Apache配置虚拟主机无效本今天电脑重新安装了一下apache,结果配置好虚拟域名之后,却一直无法访问;localhost一直是显示itworks状态;配置好了虚拟域名之后,输入任何配置的域名也都是显示itworks状态;而通过127.0.0.1是可以看到文件目录的;经过再三检查,最后发现问题是在httpd_conf文件上,只需要注释掉ServerNamelocalhos

    2022年9月18日
    1

发表回复

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

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