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

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

相关文章

IntelliJ IDEA2025创建SpringBoot项目的实现步骤

《IntelliJIDEA2025创建SpringBoot项目的实现步骤》本文主要介绍了IntelliJIDEA2025创建SpringBoot项目的实现步骤,文中通过示例代码介绍的非常详细,对大家... 目录一、创建 Spring Boot 项目1. 新建项目2. 基础配置3. 选择依赖4. 生成项目5.

Linux线程之线程的创建、属性、回收、退出、取消方式

《Linux线程之线程的创建、属性、回收、退出、取消方式》文章总结了线程管理核心知识:线程号唯一、创建方式、属性设置(如分离状态与栈大小)、回收机制(join/detach)、退出方法(返回/pthr... 目录1. 线程号2. 线程的创建3. 线程属性4. 线程的回收5. 线程的退出6. 线程的取消7.

Linux下进程的CPU配置与线程绑定过程

《Linux下进程的CPU配置与线程绑定过程》本文介绍Linux系统中基于进程和线程的CPU配置方法,通过taskset命令和pthread库调整亲和力,将进程/线程绑定到特定CPU核心以优化资源分配... 目录1 基于进程的CPU配置1.1 对CPU亲和力的配置1.2 绑定进程到指定CPU核上运行2 基于

创建Java keystore文件的完整指南及详细步骤

《创建Javakeystore文件的完整指南及详细步骤》本文详解Java中keystore的创建与配置,涵盖私钥管理、自签名与CA证书生成、SSL/TLS应用,强调安全存储及验证机制,确保通信加密和... 目录1. 秘密键(私钥)的理解与管理私钥的定义与重要性私钥的管理策略私钥的生成与存储2. 证书的创建与

Javaee多线程之进程和线程之间的区别和联系(最新整理)

《Javaee多线程之进程和线程之间的区别和联系(最新整理)》进程是资源分配单位,线程是调度执行单位,共享资源更高效,创建线程五种方式:继承Thread、Runnable接口、匿名类、lambda,r... 目录进程和线程进程线程进程和线程的区别创建线程的五种写法继承Thread,重写run实现Runnab

SpringBoot线程池配置使用示例详解

《SpringBoot线程池配置使用示例详解》SpringBoot集成@Async注解,支持线程池参数配置(核心数、队列容量、拒绝策略等)及生命周期管理,结合监控与任务装饰器,提升异步处理效率与系统... 目录一、核心特性二、添加依赖三、参数详解四、配置线程池五、应用实践代码说明拒绝策略(Rejected

Java 线程安全与 volatile与单例模式问题及解决方案

《Java线程安全与volatile与单例模式问题及解决方案》文章主要讲解线程安全问题的五个成因(调度随机、变量修改、非原子操作、内存可见性、指令重排序)及解决方案,强调使用volatile关键字... 目录什么是线程安全线程安全问题的产生与解决方案线程的调度是随机的多个线程对同一个变量进行修改线程的修改操

python如何创建等差数列

《python如何创建等差数列》:本文主要介绍python如何创建等差数列的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录python创建等差数列例题运行代码回车输出结果总结python创建等差数列import numpy as np x=int(in

怎么用idea创建一个SpringBoot项目

《怎么用idea创建一个SpringBoot项目》本文介绍了在IDEA中创建SpringBoot项目的步骤,包括环境准备(JDK1.8+、Maven3.2.5+)、使用SpringInitializr... 目录如何在idea中创建一个SpringBoot项目环境准备1.1打开IDEA,点击New新建一个项

如何使用Maven创建web目录结构

《如何使用Maven创建web目录结构》:本文主要介绍如何使用Maven创建web目录结构的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录创建web工程第一步第二步第三步第四步第五步第六步第七步总结创建web工程第一步js通过Maven骨架创pytho