呵呵,当初我学多线程时也遇到过这样的问题,也是输出的结果每次都不一样。后来我找到原因了---都是多核惹得祸。
我猜你的电脑应该也是多核的。单核的cpu在处理多线程时每次只能执行一跳指令,也就是说无论你的程序有多少个线程,每一时刻执行的也只是一个线程里的代码,cpu会轮流给每个线程分配时间片,时间片分配到哪个线程头上,哪个线程里的代码就执行。但是多核cpu就不一样了,他可以同时执行多个线程里的代码,这才是真正的“多线程”。所以你那段程序,在单核的电脑上跑应该是没有问题的,但是在多核cpu的电脑上出现的结果就会有很大的随机性。
就你贴的那张图来说,左边的运行时恰好是这样的,首先cpu1执行你主线程里的代码 在终端输出Now another thread has been created. ID =,但是由于多个cpu是同时进行的,而这时cpu2已经开始执行ThreadProc里的代码,也要开始向终端输出字符,而你的屏幕只有一个,恰好这时cpu1的时间片被移走了,所以cpu2开始执行ThreadProc里的代码向屏幕上输出,直到打完I am from a thread 17后,恰好cpu2的时间片被移走了,这时cpu1接着向屏幕打dwThreadId的值,这就出现了4660.接着又是cpu2执行完ThreadProc中剩余的代码又打了几行。
右边的这个程序运行时,恰好就是cpu1执行主线程代码输出完后,cpu2再执行线程函数中代码,符合你的预期。
但是,关键问题在于,你无法预测每个cpu的时间片分配。所以,要得到你想要的输出结果就属于随机事件了。
对与多核cpu 上的程序同步问题,最好不要用信号量,互斥量,事件对象,因为它们都属于内核对象,都是对一个cpu而言的。其他的cpu根本不会理睬你设置的这些东西。另外你的WaitForSingleObject (hThread, INFINITE); 也是在一个cpu里等待线程函数返回,对cpu2没有任何作用。
建议你用临界区(Critical Section)来实现多线程同步,因为临界区不是内核对象,他只是在进程内存中一块区域,无论有多少个cpu,任何时刻只能有一个线程访问这块内存区域,只需将你打印的部分放到临界区里就行了。
#include "stdafx.h"
#include
#include
using namespace std;
CRITICAL_SECTION g_cs;
DWORD WINAPI ThreadProc(
LPVOID lpParameter // thread data
)
{
int i=0;
while (i<20)
{
EnterCriticalSection(&g_cs);
cout<<"I am from a thread"<<" "< LeaveCriticalSection(&g_cs);
}
return 0;
}
int main(int argc, char* argv[])
{
HANDLE hThread;
DWORD dwThreadId;
InitializeCriticalSection(&g_cs);
// 创建一个线程
hThread = ::CreateThread (
NULL, // 默认安全属性
NULL, // 默认堆栈大小
ThreadProc, // 线程入口地址(执行线程的函数)
NULL, // 传给函数的参数
0, // 指定线程立即运行
&dwThreadId); // 返回线程的ID号
EnterCriticalSection(&g_cs);
cout<<"Now another thread has been created. ID ="<< dwThreadId <<"\n";
LeaveCriticalSection(&g_cs);
// 等待新线程运行结束
::WaitForSingleObject (hThread, INFINITE);
::CloseHandle (hThread);
return 0;
}
ps: kdzhy2008推荐的 侯捷 译的《win32多线程程序设计》确实是本好书,虽然是97年出版的,但是很多东西对现在还是很有启迪的。
希望对你能有所帮助,呵呵~~
这是一个线程同步问题, 关于同步, 有很多方法可以解决, 具体到你的程序,有如下方法(虽然这里有点小题大作,但是作为入门学习,对体会各种不同同步方法很有好处):
1. 最简单的方法, 在创建线程时使用CREATE_SUSPENDED标志, 在cout完想说的话后,调用ResumeThread(hThread), 激活线程
代码见图1 https://gss0.baidu.com/7LsWdDW5_xN3otqbppnN2DJv/ilovelyi/pic/item/01eae123b5ae5b854423e860.jpeg
2. 使用关键代码段CRITICAL_SECTION
处于EnterCriticalSection 和 LeaveCriticalSection之间的代码在运行完之前CPU不会切换到其他线程
代码见图2 https://gss0.baidu.com/7LsWdDW5_xN3otqbppnN2DJv/ilovelyi/pic/item/f0ed1f12979bb1a16738db60.jpeg
3. 使用事件对象
CreateEvent, WaitForSingleObject(hEvent_begin, INFINITE), SetEvent
代码见图3
https://gss0.baidu.com/7LsWdDW5_xN3otqbppnN2DJv/ilovelyi/pic/item/b90e3e10b613cc5fcb80c47b.jpeg
4. 使用信号量
CreateSemaphore, ReleaseSemaphore
代码见图4
https://gss0.baidu.com/7LsWdDW5_xN3otqbppnN2DJv/ilovelyi/pic/item/3a0c7b3717dc05100b55a95b.jpeg
5. 使用互斥对象Mutex
CreateMutex, ReleaseMutex
代码见图5
https://gss0.baidu.com/7LsWdDW5_xN3otqbppnN2DJv/ilovelyi/pic/item/11bf77f3963eef32b17ec52a.jpeg
建议你看看《win32多线程程序设计》那里面讲多线程讲的很好,你看前几页就应该明白问题所在了……
// okx.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include
#include
using namespace std;
HANDLE e_begin=NULL;
DWORD WINAPI ThreadProc(
LPVOID lpParameter // thread data
)
{
WaitForSingleObject(e_begin,INFINITE); //等待被唤醒
int i=0;
while (i<20)
{
cout<<"I am from a thread"<<" "< }
return 0;
}
int main(int argc, char* argv[])
{
HANDLE hThread;
DWORD dwThreadId;
e_begin=::CreateEvent(NULL,TRUE,FALSE,NULL); //创建事件来同步 控制子线程开启时间
hThread = ::CreateThread (
NULL, //
NULL, //
ThreadProc, //
NULL, //
0, //
&dwThreadId); //
cout<<"Now another thread has been created. ID ="<< dwThreadId <<"\n";
SetEvent(e_begin); //唤醒子线程
//
::WaitForSingleObject (hThread, INFINITE);
::CloseHandle (hThread);
::CloseHandle(e_begin);
return 0;
}