几天来,我在Windows 7 Ivy Bridge系统上创建四个线程(用于四个内核)时遇到了麻烦。我已在NASM中创建了一个简单的测试功能,以尽可能地减少演示。
程序将计数器从0递增到10亿,并返回结果。对于多核处理,我将其划分为1核心从0到250万,2核心从250万到500万,依此类推。
出于测试目的。程序将返回一个包含12个元素的数组,每个核心具有三个值。元素1-4是CreateThread返回的线程句柄;元素5-8是线程进入测试函数(Test_fn)之后从调用GetCurrentThreadId返回的值;元素9-12是每个线程执行的计算结果(从开始字节到结束字节计数)。
返回的数组(TestInfo)显示CreateThread始终成功,因为前四个元素始终包含CreateThread返回的线程句柄(在我的测试中),元素5-8始终在进入函数时从GetCurrentThreadId返回值Test_fn。问题在于元素9-12(每个核的计算结果)并不总是从Test_fn主体中的数学计算返回值,并且分布是随机的(有时核1和4将成功,有时核2和核仅3个,等等)。这意味着即使创建线程并调用Test_fn,它们也不一定总是成功运行其分配的计算。
这是从Python调用的dll,但可以从C或C ++调用。它不带参数,并且返回指向12个元素的测试数组TestInfo的指针(如上所述)。入口点是Main_Entry_fn,它调用Init_Cores_fn。线程指向Test_fn。
所以我的问题是,即使线程明确调用Test_fn,为什么线程也不总是可靠地从Test_fn返回值。
; Header Section
[BITS 64]
[default rel]
extern malloc, calloc, realloc, free
global Main_Entry_fn
export Main_Entry_fn
extern CreateThread, CloseHandle, ExitThread
extern WaitForMultipleObjects, GetCurrentThreadId
section .data align=16
const_1000000000: dq 1000000000
ThreadID: dq 0
TestInfo: times 12 dq 0
ThreadInfo: times 3 dq 0
ThreadInfo2: times 3 dq 0
ThreadInfo3: times 3 dq 0
ThreadInfo4: times 3 dq 0
ThreadHandles: times 4 dq 0
Division_Size: dq 0
Start_Byte: dq 0
End_Byte: dq 0
Return_Data_Array: times 4 dq 0
Core_Number: dq 0
section .text
; ______________________________________
Init_Cores_fn:
; Calculate the data divisions
mov rax,[const_1000000000]
mov rbx,4 ;cores
xor rdx,rdx
div rbx
mov [End_Byte],rax
mov [Division_Size],rax
mov rax,0
mov [Start_Byte],rax
; Populate the ThreadInfo arrays to pass for each core
; ThreadInfo: (1) startbyte; (2) endbyte; (3) Core_Number (0, 8, 16, 24)
mov rdi,ThreadInfo
mov rax,[Start_Byte]
mov [rdi],rax
mov rax,[End_Byte]
mov [rdi+8],rax
mov rax,[Core_Number]
mov [rdi+16],rax
call DupThreadInfo ; Create ThreadInfo arrays for cores 2-4
mov rbp,rsp ; preserve caller's stack frame
sub rsp,56 ; Shadow space (was 32)
; _____
label_0:
mov rax,[Core_Number]
cmp rax,0
jne sb2
mov rdi,ThreadInfo
jmp sb5
sb2:cmp rax,8
jne sb3
mov rdi,ThreadInfo2
jmp sb5
sb3:cmp rax,16
jne sb4
mov rdi,ThreadInfo3
jmp sb5
sb4:cmp rax,24
jne sb5
mov rdi,ThreadInfo4
sb5:
; _____
; Create Threads
mov rcx,0 ; lpThreadAttributes (Security Attributes)
mov rdx,0 ; dwStackSize
mov r8,Test_fn ; lpStartAddress (function pointer)
mov r9,rdi ; lpParameter (array of data passed to each core)
mov rax,0
mov [rsp+32],rax ; use default creation flags
mov rdi,ThreadID
mov [rsp+40],rdi ; ThreadID
call CreateThread
; Move the handle into ThreadHandles array (returned in rax)
mov rdi,ThreadHandles
mov rcx,[Core_Number]
mov [rdi+rcx],rax
mov rdi,TestInfo
mov [rdi+rcx],rax
mov rax,[Core_Number]
add rax,8
mov [Core_Number],rax
mov rbx,32 ; Four cores
cmp rax,rbx
jl label_0
; _____
; Wait
mov rcx,4 ;rax ; number of handles
mov rdx,ThreadHandles ; pointer to handles array
mov r8,0 ; wait for all threads to complete
mov r9,5000 ; milliseconds to wait
call WaitForMultipleObjects
; _____
mov rsp,rbp
jmp label_900
; ______________________________________
Test_fn:
;______
; GetCurrentThreadId
mov rdi,rcx
push rcx
call GetCurrentThreadId
mov rcx,[rdi+16] ; startbyte
mov rdi,TestInfo
mov [rdi+rcx+32],rax
pop rcx
;______
mov rdi,rcx
mov r14,[rdi] ; Start_Byte
mov r15,[rdi+8] ; End_Byte
mov r13,[rdi+16] ; Core_Number
;______
label_401:
cmp r14,r15
jge label_899
; n += 1
add r14,1
jmp label_401
;______
label_899:
mov rdi,Return_Data_Array
mov [rdi+r13],r14
mov rdi,TestInfo
mov [rdi+r13+64],r14
mov rbp,ThreadHandles
mov rax,[rbp+r13]
;mov [rdi+rbx+64],rax
call ExitThread
ret
; __________
label_900:
mov rdi,ThreadHandles
mov r8,0
label_900_01:
mov rcx,[rdi+r8]
call CloseHandle
add r8,8
cmp r8,32
jl label_900_01
mov rdi,TestInfo
mov rax,rdi
ret
; __________
; Main Entry
Main_Entry_fn:
push rdi
push rbp
call Init_Cores_fn
pop rbp
pop rdi
ret
DupThreadInfo:
mov rdi,ThreadInfo2
mov rax,8
mov [rdi+16],rax ; Core Number
mov rax,[Start_Byte]
add rax,[Division_Size]
mov [rdi],rax
mov rax,[End_Byte]
add rax,[Division_Size]
mov [rdi+8],rax
mov [Start_Byte],rax
mov rdi,ThreadInfo3
mov rax,16
mov [rdi+16],rax ; Core Number
mov rax,[Start_Byte]
mov [rdi],rax
add rax,[Division_Size]
mov [rdi+8],rax
mov [Start_Byte],rax
mov rdi,ThreadInfo4
mov rax,24
mov [rdi+16],rax ; Core Number
mov rax,[Start_Byte]
mov [rdi],rax
add rax,[Division_Size]
mov [rdi+8],rax
mov [Start_Byte],rax
ret
核心将3元素数组ThreadInfo,ThreadInfo2,ThreadInfo3和ThreadInfo4用作其数据。对于每个线程,它们包含开始编号,结束编号和核心编号乘以8:
0 250000000 0
250000000 500000000 8
500000000 750000000 16
750000000 1000000000 24
这是四个单独测试的结果:
1548 1716 1688 1768/6460 6464 6468 6472/250000000 0 0 1000000000
1744 860 1724 1668/6780 6784 6788 6792/0 0 0 1000000000
1632 1588 1488 872/7024 7028 7032 7036/0 500000000 0 0
1740 1732 1684 1536/6876 6884 6888 6880/250000000 0 750000000 0
前四个数字是每个内核的线程句柄;后四个数字是从GetCurrentThreadId到Test_fn的返回值,最后四个数字是在Test_fn中执行的简单计算的结果;它们表明,在某些情况下(但并非全部),内核返回正确的数据。
感谢任何想法。