Tutorials to .com

Tutorials to .com » Software » Asm » WIN32 compilation: 15. Multithreading tutorial

WIN32 compilation: 15. Multithreading tutorial

Print View , by: iSee ,Total views: 26 ,Word Count: 2996 ,Date: Fri, 8 May 2009 Time: 10:35 AM

15th class multi-threaded programming


This lesson, we will learn how to carry out multi-threaded programming. In addition, we will also learn how the different communications between threads.

Theory:

The previous lesson, we learn from the process, a process which includes at least one main thread. In fact, this thread is the process of implementation of a clue, apart outside the main thread to process, you can also increase the other thread, but also an increase of the implementation of other clues, which to some extent, can be seen as an application to the an increase of multi-tasking features. When the program is running, you can hang under the various conditions or to run these threads, especially in multi-CPU environment, the thread is running concurrent. These are only under the concept of W32, in WIN16 is not the same as under the concept.
In the same process in different threads to run the advantage of the process of these threads can share resources, such as global variables and resources. Of course, all threads can also have their own private stack to save the private data. In addition each thread context of the need to preserve its operation in order to be able to remember when switching threads, or the restoration of its context, of course, this is done by the operating system, is transparent to users.
We generally can be divided into two major categories of threads:
  1. To deal with the user interface thread: such threads have their own window and is responsible for processing information related to the window. WIN16 user interface thread to comply with the principle of mutually exclusive, that is not the moment only one user interface thread USER and GDI to use the core function library, which means the procedure when a user interface into the GDI or USER in the middle, which Nuclear does not allow re-entry. From this we can infer that part of the WIN95 kernel code is to comply with 16-bit mode. And WINOWS NT is pure 32-bit operating system, so the problem does not exist.
  2. Worker threads: these threads do not have to deal with a window interface, of course, also do not have to deal with news. It is generally run in the background to do some type of rough calculation, it is probably also call it the causes of worker threads now.

W32 to use the multi-threaded programming model, we can follow some kind of strategy: that is, to give only the main thread to do the work of the user interface, and other heavy work in the background by the worker thread to complete. This is like many of our daily life examples. For example: The user interface manager like a thread, which is responsible for the opinions of the public sector to the allocation of functions, and then report the results of the work to the public. And specific is the worker thread function, which is responsible for the completion of specific work orders. If the government do this to each and every specific thing, it must be another one after another, and that it can not be timely to hear public opinion and feedback. This will not be able to manage a country. Of course, even with multi-threaded system, not necessarily the government administration will be able to manage the country, but the process is multi-threaded mechanism can be used to manage the work of her own. We can call the CreateThread function to create a new thread. The function syntax is as follows:

CreateThread proto lpThreadAttributes: DWORD, \
dwStackSize: DWORD, \
lpStartAddress: DWORD, \
lpParameter: DWORD, \
dwCreationFlags: DWORD, \
lpThreadId: DWORD

Generate a thread function and generate a process of basically the same.
lpThreadAttributes -> If you'd like to have the default thread security attributes, you can buy the value is NULL.
dwStackSize -> thread stack size specified. If 0, the thread size and the same process.
lpStartAddress -> thread function start address. Attention to the function to receive only a 32-bit parameters and returns a value of 32. (This parameter can be a pointer, but the process can directly access the process of threading the definition of global variables, so you need not worry about how to not be a large number of parameters passed to the thread).
lpParameter -> passed to the thread context.
dwCreationFlags -> If it is 0, then a thread that immediately after construction started, the opposite is the flag CREATE_SUSPENDED, so you need to later show to run for the thread.
lpThreadId -> kernel thread to the new distribution of the generated thread ID.

If the successful generation of thread, CreateThread function to return a new thread on the handle. Otherwise return NULL.
If there is no designated dwCreationFlags to CREATE_SUSPENDED parameters, then the thread will run immediately. If not, we said above, need to show the start of the thread, do you need to call ResumeThread function.
After the return of the thread (the thread with the implementation of the implementation of such a function, if it is a call to the last command in the compilation is ret, then the end of the thread, unless you allow it to enter a cycle of, for example, we are talking about the user interface thread is the case, but the reason it does not withdraw from the cycle is entered in the (while (GetMessage (...))...}, if you do not have to pass it a value of 0 messages, then it can not exit), the system will automatically call ExitThread function to deal with transparent thread to pull out some of the clean-up. Of course, you can call this function, but does not seem to be very meaningful. When to get out of the exit code, you can call GetExitCodeThread function.
If you would like to end a program, you can call TerminateThread function, but the use of the function to be careful, because this function is called once the thread will exit, so that it do not have a chance to clear his own work.

Now we take a look at the communication mechanism between threads.
Overall a total of three ways:

  • The use of global variables
  • The use of Windows messaging mechanism
  • The use of incident
Above, we say that the threads will share the process resources, global variables are also included, so the thread can be through the use of global variables for communication. However, this approach is the obvious shortcomings of a number of threads access the same global variables, we must consider the problem of synchronization. For example: There are 10 members of a structure variable, one thread in the starting assignment, the assumption that only five members to update the variable's value, then kernel thread scheduling the right to deprive them of their operation to another thread, so the next thread if you want to use the overall structure of the variable, its value is clearly not on the. In addition the program multi-threaded debugging it is very difficult, especially these errors are subtle and difficult to present complex. If two threads are the user interface thread when news WINDOWS mechanism for inter-thread communication is more convenient.
You have to do is some custom windows message (and the windows do not pay attention to the news of the conflict pre-defined), and then can be passed between threads. You can define such a message, the WM_USER (it is equivalent to the value 0x0400) as the base, and then to increase the serial number sequence, such as:

WM_MYCUSTOMMSG equ WM_USER +100 h

WM_USER is less than the value of the Windows system to retain the value is greater than the value left to the user to use.
If there is a thread which is the worker thread, then it can not be used to carry out the methods of communication, and this is because there is no message queue worker thread. You should use the following strategy to carry out such workers and the user interface thread of communication between threads:

User interface Thread ------> global variable (s )----> Worker thread
Worker Thread ------> custom window message (s) ----> User interface Thread

Later on, we will explain examples of ways this communication.
Last resort is the event object. You can object to the incident as a sign. If the state of the object is no signal, it indicates that the thread is sleeping, or hang in a state of the system will not be allocated to the thread's CPU time slice. When a thread into the state to signal, WINDOWS will wake up the thread and let it operate normally.

Examples:

You can download and run the example thread1.exe, and then activate the menu item "Savage Calculation", and then the beginning of the procedure execution "add eax, eax", the implementation of a total of 600 million times, you'll find in this process, the user interface will stop responding, You can not use the menu, but also can not use the moving window. Wait until the calculation is completed, a dialog box will pop up to close the dialog box out the window before and after the first run as normal.
To avoid this inconvenience, we have calculated the work Add to a separate worker thread to main window and the only response to the user's activities. Although you can see the reaction of the user interface when the slower than usual, but can still work.

.386
. model flat, stdcall
option casemap: none
WinMain proto: DWORD,: DWORD,: DWORD,: DWORD
include \ masm32 \ include \ windows.inc
include \ masm32 \ include \ user32.inc
include \ masm32 \ include \ kernel32.inc
includelib \ masm32 \ lib \ user32.lib
includelib \ masm32 \ lib \ kernel32.lib

. const
IDM_CREATE_THREAD equ 1
IDM_EXIT equ 2
WM_FINISH equ WM_USER +100 h

. 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
mov CommandLine, eax
invoke WinMain, hInstance, NULL, CommandLine, SW_SHOWDEFAULT
invoke ExitProcess, eax

WinMain proc hInst: HINSTANCE, hPrevInst: HINSTANCE, CmdLine: LPSTR, CmdShow: DWORD
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 hInst
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, eax
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
. IF uMsg == WM_DESTROY
invoke PostQuitMessage, NULL
. ELSEIF uMsg == WM_COMMAND
mov eax, wParam
. if lParam == 0
. if ax == IDM_CREATE_THREAD
mov eax, OFFSET ThreadProc
invoke CreateThread, NULL, NULL, eax, \
0, \
ADDR ThreadID
invoke CloseHandle, eax
. else
invoke DestroyWindow, hWnd
. endif
. endif
. ELSEIF uMsg == 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

Analysis:

The main program of the main thread is a user interface thread, it is an ordinary window. The user to choose menu item "Create Thread", the procedure will generate a thread:

. if ax == IDM_CREATE_THREAD
mov eax, OFFSET ThreadProc
invoke CreateThread, NULL, NULL, eax, \
NULL, 0, \
ADDR ThreadID
invoke CloseHandle, eax
 
The code above creates a thread, the thread is the main function code ThreadProc, the function and the main thread running in parallel. After the success of the call, CreateThread function to return immediately, ThreadProc also started to run. Because we are no longer using the thread handle, we turn it off immediately in order to avoid memory leaks. We talked about the closure of the front handle will not stop the implementation of threads, but only to reduce the reference count from.

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

We can see the thread above code is just a simple count of the work done, because we have a very large base, so the thread will continue for some time you can feel, when it will be after the main thread to send WM_FINISH news. WM_FINISH news is that the definition of our own, which is defined as follows:

    WM_FINISH equ WM_USER +100 h
WM_USER news is that we can use the value of the minimum information.
It is obvious that we saw WM_FINISH, can be understood from the literal meaning of the message. Main thread receives the message will pop up a dialog box telling the user to calculate the thread is over.
Through the communication between threads, the user can repeatedly select "Create Thread", as you can run multiple threads of the calculation.
This example, the thread is a one-way communication between the. If you want the main thread can also send a message to the worker thread, then, for example, to add a menu item to control the end of worker threads, you can do:
  • add a menu item saying something like "Kill Thread" in the menu
  • a global variable which is used as a command flag. TRUE = Stop the thread, FALSE = continue the thread
  • Modify ThreadProc to check the value of the command flag in the loop.
The establishment of a global variable, when the threads start, we set its value to FALSE, when the user activated the menu item we add, the value becomes TRUE. Code segment in the threads by 1 each time ThreadProc ago, to judge the value, if TRUE, then thread on the end of the calculation of loop and exit the thread.


Assembly Language Tutorial Articles


Can't Find What You're Looking For?


Rating: Not yet rated

Comments

No comments posted.