⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 lion-tutorial15.htm

📁 内有一些代码
💻 HTM
📖 第 1 页 / 共 2 页
字号:
<html>

<head>
<link rel="stylesheet" href="../../asm.css">

<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>Iczelion's win32 asm tutorial</title>
</head>

<body bgcolor="#FFFFFF" background="../../images/back01.jpg">
<p align="center">Tutorial 15: Multithreading Programming</p>
<hr size="1">
<strong> </strong> We will learn how to create a multithreading program in this 
tutorial. We also study the communication methods between the threads. 
<p>Download the example <a href="files/tut15.zip">here</a>. 
<h3> Theory:</h3>
In previous tutorial, you learn the a process consists of at least one thread: 
the primary thread. A thread is a chain of execution. You can also create additional 
threads in your program. You can view multithreading as multitasking within one 
program. In term of implementation, a thread is a function that runs concurrently 
with the main program. You can run several instances of the same function or you 
can run several functions simultaneously depending on your requirement. Multithreading 
is specific to Win32, no Win16 counterpart exists. <br>
Threads run in the same process so they can access any resources in the process 
such as global variables, handles etc. However, each thread has its own stack 
so local variables in each thread are private. Each thread also owns its private 
register set so when Windows switches to other threads, the thread can "remember" 
its last status and can "resume" the task when it gains control again. This is 
handled internally by Windows. <br>
We can divide threads into two caterories: 
<ol>
  <li> User interface thread: This type of thread creates its own window so it 
    receives windows messages. It can respond to the user via its own window hence 
    the name. This type of thread is subject to Win16 Mutex rule which allows 
    only one user interface thread in 16-bit user and gdi kernel. While a user 
    interface thread is executing code in 16-bit user and gdi kernel, other UI 
    threads cannot use the service of the 16-bit user and gdi kernel. Note that 
    this Win16 Mutex is specific to Windows 95 since underneath, Windows 95 API 
    functions thunk down to 16-bit code. Windows NT has no Win16 Mutex so the 
    user interface threads under NT work more smoothly than under Windows 95.</li>
  <li> Worker thread: This type of thread does not create a window so it cannot 
    receive any windows message. It exists primarily to do the assigned job in 
    the background hence the name worker thread.</li>
</ol>
I advise the following strategy when using multithreading capability of Win32: 
Let the primary thread do user interface stuff and the other threads do the hard 
work in the background. In this way, the primary thread is like a Governor, other 
threads are like the Governor's staff. The Governor delegates jobs to his staff 
while he maintains contact with the public. The Governor staff obediently performs 
the work and reports back to the Governor. If the Governor were to perform every 
task himself, he would not be able to give much attention to the public or the 
press. That's akin to a window which is busy doing a lengthy job in its primary 
thread: it doesn't respond to the user until the job is completed. Such a program 
can benefit from creating an additonal thread which is responsible for the lengthy 
job, allowing the primary thread to respond to the user's commands. <br>
We can create a thread by calling CreateThread function which has the following 
syntax: 
<p><b>CreateThread proto lpThreadAttributes:DWORD,\</b> <br>
  <b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
  dwStackSize:DWORD,\</b> <br>
  <b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
  lpStartAddress:DWORD,\</b> <br>
  <b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
  lpParameter:DWORD,\</b> <br>
  <b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
  dwCreationFlags:DWORD,\</b> <br>
  <b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
  lpThreadId:DWORD</b> 
<p>CreateThread function looks a lot like CreateProcess. <br>
  <b>lpThreadAttributes</b>&nbsp; --> You can use NULL if you want the thread 
  to have default security descriptor. <br>
  <b>dwStackSize</b> --> 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. 
  <br>
  <b>lpStartAddress</b>--> 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. <br>
  <b>lpParameter</b>&nbsp; --> The parameter you want to pass to the thread function. 
  <br>
  <b>dwCreationFlags</b> --> 0 means the thread runs immediately after it's created. 
  The opposite is CREATE_SUSPENDED flag. <br>
  <b>lpThreadId</b> --> CreateThread function will fill the thread ID of the newly 
  created thread at this address. 
<p>If CreateThread call is sucessful, it returns the handle of the newly created 
  thread. Otherwise, it returns NULL. <br>
  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. <br>
  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. 
  <br>
  You can retrieve the exit code of a thread by calling GetExitCodeThread function. 
  <br>
  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. 
<p>Now let's move to the communication methods between threads. <br>
  There are three of them: 
<ul>
  <li> Using global variables</li>
  <li> Windows messages</li>
  <li> Event</li>
</ul>
Threads share the process's resources including global variables so the threads 
can use global varibles to communicate with each other. However this method must 
be used with care. Thread synchronization must enter into consideration. For example, 
if two threads use the same structure of 10 members , what happens when Windows 
suddenly yanks the control from one of the thread when it was in the middle of 
updating the structure? The other thread will be left with an inconsistent data 
in the structure! Don't make any mistake, multithreading programs are harder to 
debug and maintain. This sort of bug seems to happen at random which is very hard 
to track down. <br>
You can also use Windows messages to communicate between threads. If the threads 
are all user interface ones, there's no problem: this method can be used as a 
two-way communication. All you have to do is defining one or more custom windows 
messages that are meaningful to the threads. You define a custom message by using 
WM_USER message as the base value say , you can define it like this: 
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WM_MYCUSTOMMSG equ WM_USER+100h 
<p>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. <br>
  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: 
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
  User interface Thread ------> global variable(s)----> Worker thread <br>
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
  Worker Thread&nbsp; ------> custom window message(s) ----> User interface Thread 
<p>In fact, we will use this method in our example. <br>
  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. 
<h3> Example:</h3>
You should download the example zip file and run thread1.exe. Click the "Savage 
Calculation" menu item. This will instruct the program to perform "add eax,eax 
" for 600,000,000 times. Note that during that time, you cannot do anything with 
the main window: you cannot move it, you cannot activate its menu, etc. When the 
calculation is completed, a message box appears. After that the window accepts 
your command normally. <br>
To avoid this type of inconveniece to the user, we can move the "calculation" 
routine into a separate worker thread and let the primary thread continue with 
its user interface task. You can see that even though the main window responds 
more slowly than usual,&nbsp; it still responds 
<p><b>.386</b> <br>
  <b>.model flat,stdcall</b> <br>
  <b>option casemap:none</b> <br>
  <b>WinMain proto :DWORD,:DWORD,:DWORD,:DWORD</b> <br>
  <b>include \masm32\include\windows.inc</b> <br>
  <b>include \masm32\include\user32.inc</b> <br>
  <b>include \masm32\include\kernel32.inc</b> <br>
  <b>includelib \masm32\lib\user32.lib</b> <br>
  <b>includelib \masm32\lib\kernel32.lib</b> 
<p><b>.const</b> <br>
  <b>IDM_CREATE_THREAD equ 1</b> <br>
  <b>IDM_EXIT equ 2</b> <br>
  <b>WM_FINISH equ WM_USER+100h</b> 
<p><b>.data</b> <br>
  <b>ClassName db "Win32ASMThreadClass",0</b> <br>
  <b>AppName&nbsp; db "Win32 ASM MultiThreading Example",0</b> <br>
  <b>MenuName db "FirstMenu",0</b> <br>
  <b>SuccessString db "The calculation is completed!",0</b> 
<p><b>.data?</b> <br>
  <b>hInstance HINSTANCE ?</b> <br>
  <b>CommandLine LPSTR ?</b> <br>
  <b>hwnd HANDLE ?</b> <br>
  <b>ThreadID DWORD ?</b> 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -