线程创建后,未必立即运行

2023-12-11 05:18

本文主要是介绍线程创建后,未必立即运行,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 

    线程创建,并不立即执行,而是等时间片到来后再执行。

 

     一个进程,包含多个线程。

      则这个线程共享进程的数据等资源,各个线程呈现并发执行状态。

      线程执行时,所传入的参数值是执行时才传入的,而不是创建线程时传入的。

 

 

考虑以下程序:

 

#include "stdafx.h"
#include <iostream>
#include <Windows.h>
#include <process.h>
using namespace std;
long g_nNum;
unsigned int _stdcall ThreadFun(void *pPM);
const int THREAD_NUM=10;
CRITICAL_SECTION g_csThreadCore;
HANDLE hSemaphore;


 

int main(array<System::String ^> ^args)
{
g_nNum=0;
HANDLE handle[10];
int i=0;
//初始化关键段
InitializeCriticalSection(&g_csThreadCore);
//创建信号量
hSemaphore=CreateSemaphore(NULL,0,1,NULL);
while(i<THREAD_NUM)
{
handle[i]=(HANDLE)_beginthreadex(NULL,0,ThreadFun,&i,0,NULL);
//	WaitForSingleObject(hSemaphore,INFINITE);
i++;
}
WaitForMultipleObjects(THREAD_NUM,handle,TRUE,INFINITE);
DeleteCriticalSection(&g_csThreadCore);
CloseHandle(hSemaphore);
cin>>i;
return 0;
}
unsigned int _stdcall  ThreadFun(void *pPM)
{
int nThreadnum=*(int*)pPM;
ReleaseSemaphore(hSemaphore,1,NULL);
//Sleep(50);
EnterCriticalSection(&g_csThreadCore);
g_nNum++;
//	Sleep(50);
printf("线程编号为%d  全局资源值为%d\n",nThreadnum,g_nNum);
LeaveCriticalSection(&g_csThreadCore);
return 0;
}


 

  

运行结果:

 

线程编号为7  全局资源值为1
线程编号为7  全局资源值为2
线程编号为8  全局资源值为3
线程编号为7  全局资源值为4
线程编号为7  全局资源值为5
线程编号为7  全局资源值为6
线程编号为10  全局资源值为7
线程编号为10  全局资源值为8
线程编号为7  全局资源值为9
线程编号为7  全局资源值为10

由此可见,线程编号不正确,原因是:

        创建线程后,不立即执行, 当执行时,传入参数的值已经改变。

        而且,线程编号有重复值, 这说明在i值改变前,启动了好几个线程, 传入这些线程的值都是i(编号),因此才会出现线程编号重复问题。

        比如: i==0时,创建线程0

                      i==1时,创建线程1

                      i==2时,创建线程2

         但,此时还没有一个线程执行,当i=3时,创建线程3,

         此时,时间片轮到线程了,因此可能线程0开始执行,但执行线程0时,传入的为当前i的值3, 因此线程0的编号被误改为3,这个线程未执行完,又启动了其他线程,比如线程1,但线程1传入的参数也是i的当前值3

 

如何才能让线程编号正确?

之所以不正确显示,是因为线程执行时,未能传入正确的i值。

因此,如果线程创建成功执行时,能传入正确值的话,这个问题就解决了。

怎样传入正确值呢?

handle[i]=(HANDLE)_beginthreadex(NULL,0,ThreadFun,&i,0,NULL);

i++;

创建线程i,此线程创建后,并没有立即执行,而是直接i++ 然后创建另一个线程。

如果在i++之前,能确保此线程执行的话,则能传入正确的i值。。。。

 

  handle[i]=(HANDLE)_beginthreadex(NULL,0,ThreadFun,&i,0,NULL);
  WaitForSingleObject(hSemaphore,INFINITE);
  i++;

 

WaitForSingleObject(hSemaphore,INFINITE);

直到hSemaphore有信号时,才往下继续执行,而只有当创建的线程执行时,Semaphore才有信号。

因为线程函数中有 设置信号的函数: ReleaseSemaphore(hSemaphore,1,NULL);

 

这样就保证了线程编号的正确。

 

int main(array<System::String ^> ^args)
{
g_nNum=0;
HANDLE handle[10];
int i=0;
//初始化关键段
InitializeCriticalSection(&g_csThreadCore);
//创建信号量
hSemaphore=CreateSemaphore(NULL,0,1,NULL);
while(i<THREAD_NUM)
{
handle[i]=(HANDLE)_beginthreadex(NULL,0,ThreadFun,&i,0,NULL);
WaitForSingleObject(hSemaphore,INFINITE);
i++;
}
WaitForMultipleObjects(THREAD_NUM,handle,TRUE,INFINITE);
DeleteCriticalSection(&g_csThreadCore);
CloseHandle(hSemaphore);
cin>>i;
return 0;
}


线程编号为0  全局资源值为1
线程编号为1  全局资源值为2
线程编号为2  全局资源值为3
线程编号为3  全局资源值为4
线程编号为4  全局资源值为5
线程编号为5  全局资源值为6
线程编号为6  全局资源值为7
线程编号为7  全局资源值为8
线程编号为8  全局资源值为9
线程编号为9  全局资源值为10

注意:线程函数除设置信号量外,还设置了临界区,临界区至多只有一个线程访问。

当一个线程进入临界区后,其它线程只能等待它退出临界区后,再访问。

这保证了,临界区内的全局数据不被多个线程同时修改,保证了临界区内的程序顺序执行

 

	EnterCriticalSection(&g_csThreadCore);
g_nNum++;
printf("线程编号为%d  全局资源值为%d\n",nThreadnum,g_nNum);
LeaveCriticalSection(&g_csThreadCore);


假如: 去掉临界区

线程编号为3  全局资源值为4
线程编号为2  全局资源值为1
线程编号为0  全局资源值为2
线程编号为1  全局资源值为3
线程编号为8  全局资源值为5
线程编号为9  全局资源值为6
线程编号为5  全局资源值为8
线程编号为7  全局资源值为10
线程编号为6  全局资源值为7
线程编号为4  全局资源值为9

由此可见,全局资源顺序有误,甚至会有重复现象

这是因为:各个线程 同时访问全局变量,所导致的不一致

 

 

参考资料: http://blog.sina.com.cn/s/blog_6120646301012szj.html

这篇关于线程创建后,未必立即运行的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/479564

相关文章

Window Server创建2台服务器的故障转移群集的图文教程

《WindowServer创建2台服务器的故障转移群集的图文教程》本文主要介绍了在WindowsServer系统上创建一个包含两台成员服务器的故障转移群集,文中通过图文示例介绍的非常详细,对大家的... 目录一、 准备条件二、在ServerB安装故障转移群集三、在ServerC安装故障转移群集,操作与Ser

Window Server2016 AD域的创建的方法步骤

《WindowServer2016AD域的创建的方法步骤》本文主要介绍了WindowServer2016AD域的创建的方法步骤,文中通过图文介绍的非常详细,对大家的学习或者工作具有一定的参考学习价... 目录一、准备条件二、在ServerA服务器中常见AD域管理器:三、创建AD域,域地址为“test.ly”

Python在固定文件夹批量创建固定后缀的文件(方法详解)

《Python在固定文件夹批量创建固定后缀的文件(方法详解)》文章讲述了如何使用Python批量创建后缀为.md的文件夹,生成100个,代码中需要修改的路径、前缀和后缀名,并提供了注意事项和代码示例,... 目录1. python需求的任务2. Python代码的实现3. 代码修改的位置4. 运行结果5.

使用IntelliJ IDEA创建简单的Java Web项目完整步骤

《使用IntelliJIDEA创建简单的JavaWeb项目完整步骤》:本文主要介绍如何使用IntelliJIDEA创建一个简单的JavaWeb项目,实现登录、注册和查看用户列表功能,使用Se... 目录前置准备项目功能实现步骤1. 创建项目2. 配置 Tomcat3. 项目文件结构4. 创建数据库和表5.

使用SpringBoot创建一个RESTful API的详细步骤

《使用SpringBoot创建一个RESTfulAPI的详细步骤》使用Java的SpringBoot创建RESTfulAPI可以满足多种开发场景,它提供了快速开发、易于配置、可扩展、可维护的优点,尤... 目录一、创建 Spring Boot 项目二、创建控制器类(Controller Class)三、运行

Springboot的ThreadPoolTaskScheduler线程池轻松搞定15分钟不操作自动取消订单

《Springboot的ThreadPoolTaskScheduler线程池轻松搞定15分钟不操作自动取消订单》:本文主要介绍Springboot的ThreadPoolTaskScheduler线... 目录ThreadPoolTaskScheduler线程池实现15分钟不操作自动取消订单概要1,创建订单后

JAVA中整型数组、字符串数组、整型数和字符串 的创建与转换的方法

《JAVA中整型数组、字符串数组、整型数和字符串的创建与转换的方法》本文介绍了Java中字符串、字符数组和整型数组的创建方法,以及它们之间的转换方法,还详细讲解了字符串中的一些常用方法,如index... 目录一、字符串、字符数组和整型数组的创建1、字符串的创建方法1.1 通过引用字符数组来创建字符串1.2

Linux使用nohup命令在后台运行脚本

《Linux使用nohup命令在后台运行脚本》在Linux或类Unix系统中,后台运行脚本是一项非常实用的技能,尤其适用于需要长时间运行的任务或服务,本文我们来看看如何使用nohup命令在后台... 目录nohup 命令简介基本用法输出重定向& 符号的作用后台进程的特点注意事项实际应用场景长时间运行的任务服

如何在一台服务器上使用docker运行kafka集群

《如何在一台服务器上使用docker运行kafka集群》文章详细介绍了如何在一台服务器上使用Docker运行Kafka集群,包括拉取镜像、创建网络、启动Kafka容器、检查运行状态、编写启动和关闭脚本... 目录1.拉取镜像2.创建集群之间通信的网络3.将zookeeper加入到网络中4.启动kafka集群

C语言线程池的常见实现方式详解

《C语言线程池的常见实现方式详解》本文介绍了如何使用C语言实现一个基本的线程池,线程池的实现包括工作线程、任务队列、任务调度、线程池的初始化、任务添加、销毁等步骤,感兴趣的朋友跟随小编一起看看吧... 目录1. 线程池的基本结构2. 线程池的实现步骤3. 线程池的核心数据结构4. 线程池的详细实现4.1 初