Download the example here.
HANDLE CreateThread(
   
LPSECURITY_ATTRIBUTES  lpThreadAttributes,    
// address of thread security attributes
   
DWORD  dwStackSize,                                                   
// initial thread stack size, in bytes
   
LPTHREAD_START_ROUTINE  lpStartAddress,     
// address of thread function
    LPVOID  lpParameter,                                                   
// argument for new thread
    DWORD  dwCreationFlags,                                           
// creation flags
   
LPDWORD  lpThreadId                                                
// address of returned thread identifier
  
);
CreateThread function looks a lot like CreateProcess.
lpThreadAttributes 
--> You can use NULL if you want the thread to have default security descriptor.
dwStackSize 
--> specify the stack size of the thread.
If you want the thread to have the same stack size as the primary thread,
use NULL as this parameter.
lpStartAddress
--> Address of the thread function.It's the
function that will perform the work of the thread. This function MUST receive
one and only one 32-bit parameter and return a 32-bit value.
lpParameter 
--> The parameter you want to pass to the thread function.
dwCreationFlags
--> The same as those in CreateProcess function.
lpThreadId 
--> CreateThread function will fill the thread
ID of the newly created thread at this address.
If CreateThread call is sucessful, it returns
the handle of the newly created thread. Otherwise, it returns NULL.
The thread function runs as soon as CreateThread
call is success ful unless you specify CREATE_SUSPENDED flag in dwCreationFlags.
In that case, the thread is suspended until ResumeThread function is called.
When the thread function returns with ret instruction,
Windows calls ExitThread function for the thread function implicitly. You
can call ExitThread function with in your thread function yourself but
there' s little point in doing so.
You can retrieve the exit code of a thread by
calling GetExitCodeThread function.
If you want to terminate a thread from other
thread, you can call TerminateThread function. But you should use this
function under extreme circumstance since this function terminates the
thread immediately without giving the thread any chance to clean up after
itself.
Now let's move to the communication methods between
threads.
There are three of them:
WM_MYCUSTOMMSG equ WM_USER+100h
Windows will not use any value from WM_USER upward
for its own messages so you can use the value WM_USER and above as your
own custom message value.
If one of the thread is a user interface thread
and the other is a worker one, you cannot use this method as two-way communication
since a worker thread doesn't have its own window so it doesn't have a
message queue. You can use the following scheme:
                           
User interface Thread ------> global variable(s)----> Worker thread
                           
Worker Thread  ------> custom window message(s) ----> User interface
Thread
In fact, we will use this method in our example.
The last communication method is an event object.
You can view an event object as a kind of flag. If the event object is
in "unsignalled" state, the thread is dormant or sleeping, in this state,
the thread doesn't receive CPU time slice. When the event object is in
"signalled" state,Windows "wakes up" the thread and it starts performing
the assigned task.
include windows.inc
includelib user32.lib
includelib kernel32.lib
includelib gdi32.lib
.const
IDM_CREATE_THREAD equ 1
IDM_EXIT equ 2
WM_FINISH equ WM_USER+100h
.data
ClassName db "Win32ASMThreadClass",0
AppName  db "Win32 ASM MultiThreading
Example",0
MenuName db "FirstMenu",0
SuccessString db "The calculation is completed!",0
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hwnd HANDLE ?
ThreadID DWORD ?
.code
start:
    invoke GetModuleHandle,
NULL
    mov    hInstance,eax
    invoke GetCommandLine
    invoke WinMain, hInstance,NULL,CommandLine,
SW_SHOWDEFAULT
    invoke ExitProcess,eax
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:SDWORD
    LOCAL wc:WNDCLASSEX
    LOCAL msg:MSG
    mov   wc.cbSize,SIZEOF
WNDCLASSEX
    mov   wc.style,
CS_HREDRAW or CS_VREDRAW
    mov   wc.lpfnWndProc,
OFFSET WndProc
    mov   wc.cbClsExtra,NULL
    mov   wc.cbWndExtra,NULL
    push  hInstance
    pop   wc.hInstance
    mov   wc.hbrBackground,COLOR_WINDOW+1
    mov   wc.lpszMenuName,OFFSET
MenuName
    mov   wc.lpszClassName,OFFSET
ClassName
    invoke LoadIcon,NULL,IDI_APPLICATION
    mov   wc.hIcon,eax
    mov   wc.hIconSm,0
    invoke LoadCursor,NULL,IDC_ARROW
    mov   wc.hCursor,eax
    invoke RegisterClassEx,
addr wc
    invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR
ClassName,ADDR AppName,\
          
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
          
CW_USEDEFAULT,300,200,NULL,NULL,\
          
hInst,NULL
    mov   hwnd,eax
    invoke ShowWindow, hwnd,SW_SHOWNORMAL
    invoke UpdateWindow, hwnd
    .WHILE TRUE
           
invoke GetMessage, ADDR msg,NULL,0,0
           
.BREAK .IF (!eax)
           
invoke TranslateMessage, ADDR msg
           
invoke DispatchMessage, ADDR msg
    .ENDW
    mov    
eax,msg.wParam
    ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM,
lParam:LPARAM
    mov   eax,uMsg
    .IF eax==WM_DESTROY
       
invoke PostQuitMessage,NULL
    .ELSEIF eax==WM_COMMAND
       
mov eax,wParam
       
.if lParam==0
           
.if ax==IDM_CREATE_THREAD
               
mov  eax,OFFSET ThreadProc
               
invoke CreateThread,NULL,NULL,eax,\
                                       
NULL,NORMAL_PRIORITY_CLASS,\
                                       
ADDR ThreadID
               
invoke CloseHandle,eax
           
.else
               
invoke DestroyWindow,hWnd
           
.endif
       
.endif
    .ELSEIF eax==WM_FINISH
       
invoke MessageBox,NULL,ADDR SuccessString,ADDR AppName,MB_OK
    .ELSE
       
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
       
ret
    .ENDIF
    xor    eax,eax
    ret
WndProc endp
ThreadProc PROC USES ecx Param:DWORD
       
mov  ecx,600000000
Loop1:
       
add  eax,eax
       
dec  ecx
       
jz   Get_out
       
jmp  Loop1
Get_out:
       
invoke PostMessage,hwnd,WM_FINISH,NULL,NULL
       
ret
ThreadProc ENDP
end start
 
           
.if ax==IDM_CREATE_THREAD
               
mov  eax,OFFSET ThreadProc
               
invoke CreateThread,NULL,NULL,eax,\
                                       
NULL,NORMAL_PRIORITY_CLASS,\
                                       
ADDR ThreadID
               
invoke CloseHandle,eax
  
The above function creates a thread that will
run a procedure named ThreadProc concurrently with the primary thread.
After the successful call, CreateThread returns immediately and ThreadProc
begins to run. Since we do not use thread handle, we should close it else
there'll be some leakage of memory.
ThreadProc PROC USES ecx Param:DWORD
       
mov  ecx,600000000
Loop1:
       
add  eax,eax
       
dec  ecx
       
jz   Get_out
       
jmp  Loop1
Get_out:
       
invoke PostMessage,hwnd,WM_FINISH,NULL,NULL
       
ret
ThreadProc ENDP
As you can see, ThreadProc performs a savage calculation which takes quite a while to finish and when it finishs it posts a WM_FINISH message to the main window. WM_FINISH is our custom message defined like this: