У меня есть код в NASM (64 бит) в Windows для запуска четырех одновременных потоков (каждый из которых назначен отдельному ядру) на четырехъядерном компьютере с Windows x86-64.
Потоки создаются в цикле.После создания потока он вызывает WaitForMultipleObjects для координации потоков.Вызываемая функция - Test_Function (см. Код ниже).
Каждый поток (ядро) выполняет Test_Function для большого массива.Первое ядро начинается с нуля элемента данных, второе ядро начинается с 1, третье ядро начинается с 2, четвертое ядро начинается с 3, а каждое ядро увеличивается на четыре (например, 0, 4, 8, 12).
В Test_Function я создал небольшую тестовую программу, которая записывает одно из значений входных данных в местоположение, соответствующее его начальному байту, чтобы убедиться, что я успешно создал четыре потока и они возвращают правильные данные.
Каждый поток должен записать значение шага (32), но тест показывает, что четыре поля заполняются случайным образом, причем некоторые поля отображаются как ноль.Если я повторяю тест несколько раз, я вижу, что нет последовательности, для которой поля будут иметь значение 32 (остальные всегда показывают как 0).Это может быть побочным эффектом WaitForMultipleObjects, но я не видел ничего в документах, чтобы подтвердить это.
Кроме того, WaitForMultipleObjects ожидает в ThreadHandles, возвращаемых CreateThread;когда я проверяю массив ThreadHandles, он всегда показывает так: 268444374, 32, 1652, 1584. Только первый элемент выглядит как размер дескриптора, остальные не выглядят как значения дескриптора.
Одна из возможностей состоит в том, что два параметра, передаваемые в стеке, могут находиться не в правильных местах:
mov rax,0
mov [rsp+40],rax ; use default creation flags
mov rax,[ThreadCount]
mov [rsp+32],rax ; ThreadID
Согласно документам, ThreadCount должен быть указателем.Когда я меняю строку на mov rax, ThreadCount (значение указателя), программа вылетает.Когда я изменяю его на:
mov rax,0
mov [rsp+32],rax ; use default creation flags
mov rax,ThreadCount
mov [rsp+40],rax ; ThreadID
, теперь он надежно обрабатывает первый поток, но не потоки 2-4.
Таким образом, суть в том, что потоки создаются, но они выполняются случайным образом, причем некоторые потоки не выполняются вообще, в произвольном порядке.Когда я изменяю параметры CreateThread (как показано выше), выполняется первый поток, но не потоки 2-4.
Вот тестовый код, показывающий соответствующие части.Если нужен воспроизводимый пример, я могу его подготовить.
Спасибо за любые идеи.
Init_Cores_fn:
; EACH OF THE CORES CALLS Test_Function AND EXECUTES THE WHOLE PROGRAM.
; WE PASS THE STARTING BYTE (0, 8, 16, 24) AND THE "STRIDE" = NUMBER OF CORES.
; ON RETURN, WE SYNCHRONIZE ANY DATA. ON ENTRY TO EACH CORE, SET THE REGISTERS
; Populate the ThreadInfo array with vars to pass
; ThreadInfo: length, startbyte, stride, vars into registers on entry to each core
mov rdi,ThreadInfo
mov rax,ThreadInfoLength
mov [rdi],rax
mov rax,[stride]
mov [rdi+16],rax ; 8 x number of cores (32 in this example)
; Register Vars
mov [rdi+24],r15
mov [rdi+32],r14
mov [rdi+40],r13
mov [rdi+48],r12
mov [rdi+56],r10
mov rbp,rsp ; preserve caller's stack frame
sub rsp,56 ; Shadow space
; _____
label_0:
mov rdi,ThreadInfo
mov rax,[FirstByte]
mov [rdi+8],rax ; 0, 8, 16, or 24
; _____
; Create Threads
mov rcx,0 ; lpThreadAttributes (Security Attributes)
mov rdx,0 ; dwStackSize
mov r8,Test_Function ; lpStartAddress (function pointer)
mov r9,ThreadInfo ; lpParameter (array of data passed to each core)
mov rax,0
mov [rsp+40],rax ; use default creation flags
mov rax,[ThreadCount]
mov [rsp+32],rax ; ThreadID
call CreateThread
; Move the handle into ThreadHandles array (returned in rax)
mov rdi,ThreadHandles
mov rcx,[FirstByte]
mov [rdi+rcx],rax
mov rax,[FirstByte]
add rax,8
mov [FirstByte],rax
mov rax,[ThreadCount]
add rax,1
mov [ThreadCount],rax
mov rbx,4
cmp rax,rbx
jl label_0
; _____
; Wait
mov rcx,rax ; number of handles
mov rdx,ThreadHandles ; pointer to handles array
mov r8,1 ; wait for all threads to complete
mov r9,1000 ; milliseconds to wait
call WaitForMultipleObjects
; _____
;[ Code HERE to do cleanup if needed after the four threads finish ]
mov rsp,rbp
jmp label_900
; __________________
; The function for all threads to call
Test_Function:
; Populate registers
mov rdi,rcx
mov rax,[rdi]
mov r15,[rdi+24]
mov rax,[rdi+8] ; start byte
mov r13,[rdi+40]
mov r12,[rdi+48]
mov r10,[rdi+56]
xor r11,r11
xor r9,r9
pxor xmm15,xmm15
pxor xmm15,xmm14
pxor xmm15,xmm13
; Now test it - BUT the first thread does not write data
mov rcx,[rdi+8] ; start byte
mov rax,[rdi+16] ; stride
cvtsi2sd xmm0,rax
movsd [r15+rcx],xmm0
ret