Multithreading Programming Technology and Its Implementation in Windows

Source: Internet
Author: User

This article first discusses the concept of threads not available in 16-bit Windows, then focuses on the multi-thread programming technology in 32-bit Windows 95 environment, and finally provides an example using this technology, that is, the realization of the Videophone Based on TCP/IP in Windows 95.

I. Question proposal

Recently, the author encountered such a problem during the development of Internet-based videophone. In the Internet-based videophone system, speech collection, speech codec, image collection, image codec, voice and image code streams must be transmitted at the same time, both must be processed in parallel. Especially for voice signals, if the image codec process takes too long and the voice signal cannot be served, the call will be interrupted. If the image or voice processing time is too long, and the stream data cannot be transmitted in time, communication is also interrupted. In this way, we need to implement a parallel programming. On a machine with only one CPU, that is, we need to allocate the CPU time to each event according to certain priority rules, regular processing of an event rather than processing it too long. In 32-bit Windows 95 or Windows SNT, we can use multi-thread programming technology to implement this parallel programming. In fact, this type of parallel programming is required in many cases. For example, when FileManager copies a file, it displays a dialog box listing the names of the source and target files and contains a Cancel button. If you click the "Cancel" button during the file copy process, the copy will be terminated.

In a 16-bit Windows system, you must periodically call the PeekMessage function within the FileCopy loop. If you are reading a large data block, the button action can be responded only after the block is read. It takes several seconds to read the file from a floppy disk, because the machine is too slow to respond, you will frequently place this button, thinking that the system does not know you want to terminate this operation. If you put the FileCopy command into another thread, you do not need to put a lot of PeekMessage functions in the Code. The thread processing the user interface will be operated separately from it, click the "Cancel" button in. Similarly, it is useful to create a separate thread in the application to process all print tasks. In this way, you can continue to use the application during print processing.

Ii. Concept of Thread

To understand the concept of a thread, we must first discuss the concept of a process.

A process is usually defined as an instance of a program. In Win32, the process occupies 4 GB of address space. Win32 processes are not dynamic, unlike they are in MS-DOS and a 16-bit Windows operating system. That is to say, a Win32 process does not execute any commands, but occupies a 4 GB address space, which contains the code and data of the application EXE file. Any DLL required by EXE also loads their code and data into the address space of the process. In addition to address space, a process also occupies some resources, such as files, dynamic memory allocation, and threads. When a process is terminated, various resources created during its lifecycle are cleared.

But the process is not dynamic. It is just a static concept. In order for the process to complete some work, the process must have at least one thread. Therefore, the thread describes the execution in the process. It is precisely the thread that is responsible for executing the Code contained in the address space of the process. In fact, a single process can contain several threads that can simultaneously execute code in the address space of the process. To do this, each thread has its own set of CPU registers and stacks.

Each process has at least one thread executing the code in its address space. If there is no thread executing the code in the process address space, the process will not continue to exist, the system automatically clears the process and its address space. To run all these threads, the Operating System Schedules some CPU time for each independent thread. The operating system provides the time slice to the thread in rotation mode, which gives an illusion, it seems that these threads are running at the same time. When a Win32 process is created, its first thread is called the main thread, which is automatically generated by the system and can then generate additional threads by the main thread, more threads can be generated.

Iii. Thread Programming Technology

1. Write thread Functions

All threads must start from a specified number of functions. This function is called a thread function and must have the following prototype:

DWORDWINAPIYourThreadFunc (LPVOIDlpvThreadParm );

This function inputs an LPVOID parameter, which can be a DWORD integer or a pointer to a buffer and return a DWORD Value. Like the WinMain function, this function is not called by the operating system. The operating system calls an internal function including non-C runtime in KERNEL32.DLL, such as StartOfThread, then, after the StartOfThread function establishes an exception handling framework, it calls our function.

2. Create a thread

The main thread of a process is automatically generated by the operating system. If you want a main thread to create additional threads, you can call CreateThread to complete the process.

HANDLECreateThread (LPSECURITY_ATTRIBUTES lpsa, DWORDcbstack, LPTHREAD_START_ROUTINElpStartAddr,
LPVOID lpvThreadParm, DWORDfdwCreate, LPDWORDlpIDThread );

The lpsa parameter is a pointer to the SECURITY_ATTRIBUTES structure. If you want to set an object as the default security attribute, you can pass a NULL value. If you want any sub-process to inherit a thread object handle, you must specify a SECURITY_ATTRIBUTES structure, the bInheritHandle member is initialized to TRUE. The cbstack parameter indicates the address space allocated by the thread for the stack it uses. The value 0 indicates that the default value is used.

The lpStartAddr parameter indicates the address of the function where the new thread starts to execute the time code, that is, the thread function. LpvThreadParm is the parameter for passing in the thread function. The fdwCreate parameter specifies the additional flag for controlling the thread creation. Two values can be used. If this parameter is set to 0, the thread starts execution immediately. If this parameter is create_suincluded, the system initializes the CPU and registers the members of The CONTEXT structure after the thread is generated, prepare to execute the first instruction in the thread function, but suspend the thread rather than immediately. The last parameter lpIDThread is a DWORD address and returns the ID value assigned to the new thread.

3. Terminate the thread

If a thread calls the ExitThread function, it can terminate itself.

VOIDExitThread (UINTfuExitCode );

This function terminates the thread after the exit code fuExitCode is set for the thread that calls the function. You can call the TerminateThread function to terminate a thread.

BOOLTerminateThread (HANDLE hThread, DWORDdwExitCode );

This function is used to end the thread specified by the hThread parameter and set dwExitCode to the exit code of the thread. When a thread does not respond, we can use other threads to call this function to terminate this unresponsive thread.

4. Set the relative priority of the thread

When a thread is created for the first time, its priority is equal to the priority of the process to which it belongs. In a single process, you can call the SetThreadPriority function to change the relative priority of a thread. The priority of a thread is relative to the priority of the process to which it belongs.

BOOLSetThreadPriority (HANDLE hThread, intnPriority );

The hThread parameter points to the handle of the priority thread to be modified. nPriority can be the following values:

THREAD_PRIORITY_LOWEST,
THREAD_PRIORITY_BELOW_NORMAL,
THREAD_PRIORITY_NORMAL,
THREAD_PRIORITY_ABOVE_NORMAL,
THREAD_PRIORITY_HIGHEST

5. Thread suspension and recovery

Previously I mentioned that you can create a thread in the suspended state (implemented by passing the create_suincluded flag to the CreateThread function ). When you do this, the system creates the core object of the specified thread, creates the thread stack, and initializes the thread CPU registration member in the CONTEXT structure. However, the thread object is assigned an initial pending Count value of 1, which indicates that the system will no longer allocate CPU to the execution thread. To start executing a thread, the other thread must call ResumeThread and pass it to the thread handle returned when CreateThread is called.

DWORD ResumeThread (HANDLEhThread );

A thread can be suspended for multiple times. If a thread is suspended three times, the thread must be restored three times before it is allocated CPU. In addition to using the create_suincluded flag when creating a thread, you can also use the SuspendThread function to suspend the thread.

DWORDSuspendThread (HANDLE hThread );

Iv. Application of multi-thread programming technology

As I mentioned earlier, to implement a Videophone Based on TCP/IP, it is necessary to perform speech collection, speech codec, image collection, image codec, and code stream data receiving and sending in parallel. Voice and image acquisition is performed by the hardware acquisition card. Our program only needs to initialize the hardware acquisition card and then read the collected data in real time, however, the encoding and decoding of voice and image data and the transmission of code stream data must be coordinated and executed by the program. The processing of a certain event must not be too long. The CPU must take turns to serve each event, the thread in Windows 95 meets this requirement.

Below I will show some source code of TCP/IP-based videophone implemented by using multithreading Programming Technology in Windows 95 environment, including the main window process function, and the call end and the called end's TCP/IP receiving thread function and the speech codec thread function. Because the real-time performance of image codec is lower than that of the speech processing and transmission modules, I use the speech codec as an event to query image data and then perform image codec, instead of implementing a single thread for image codec.

During initialization of the main window, I used the create_suincluded flag to create two threads hThreadG7231 and hThreadTCPRev. A thread function used for speech codec is G723Proc. This thread constantly queries whether there are locally compiled speech and image code streams. If so, it is packaged in H.223, then, it is sent to the other party through the TCP port. Another thread is used to receive TCP/IP requests. Its thread function is AcceptThreadProcRev. This thread continuously detects whether the TCP/IP Port has a stream sent from the other side. If yes, it receives the stream, after H.223 decoding, it is sent to the corresponding buffer. The buffer content is queried by the speech codec thread G723Proc and sent to the corresponding decoder. The multi-threaded programming technology enables the operating system to regularly serve the language-Audio Codec Module and transmission module, thus ensuring that communication is not interrupted.

V. program source code:

// The Window Process Based on the TCP/IP videophone Main Window
Long apientry MainWndProc (HWND hWnd, UINT message, UINT wParam, LONG lParam)
{
Static HANDLE hThreadG7231, hThreadTCPListen, hThreadTCPRev;
DWORDThreadIDG7231, ThreadIDTCPListen, ThreadIDTCPRev;
Static THREADPACK tp;
Static THREADPACK tp1;
Unsigned char Buf [80];
CAPSTATUS capStatus;
Switch (message)
{
Case WM_CREATE:
Init_Wsock (hWnd); // initialize some data structures
Init_BS (2, & bs );
Vd_tx_pdu.V_S = 0; vd_tx_pdu.N_S = 0;
Vd_rx_pdu.V_R = 0; vd_tx_sdu.bytes = 0;
If (dnldProg (hWnd, "h324g723.exe "))
{
// Mount the DSP core of the Speech Codec
MessageBox (hWnd, "Load G.723.1 Kernel Error", "Error", MB_ OK );
PostQuitMessage (0 );}
Else
MessageBox (hWnd, "Load G.723.1 Kernel OK! "," Indication ", MB_ OK );
// Create a speech codec thread
Parag7231.hWnd = hWnd;
HThreadG7231 = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE) G723Proc,
(G7231DATA *)? G7231,
Create_suincluded, (LPDWORD) & ThreadIDG7231 );
If (! HThreadG7231)
{
Wsprintf (Buf, "Error in creating G7231 thread: % d", GetLastError ());
MessageBox (hWnd, Buf, "WM_CREATE", MB_ OK );}
// Create a TCP/IP receiving thread
Tp1.hWnd = hWnd;
HThreadTCPRev = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE) AcceptThreadProcRev,
(G7231DATA *) & tp1, create_suincluded,
(LPDWORD) & ThreadIDTCPRev );
If (! HThreadTCPRev)
{
Wsprintf (Buf, "Error in creating TCP Receive thread: % d", GetLastError ());
MessageBox (hWnd, Buf, "WM_CREATE", MB_ OK );}
// Start listening to the network
SendMessage (hWnd, WM_COMMAND, IDM_LISTEN, NULL );
Break;
Case WM_VIDEO_ENCODE: // Image Encoding
If (needencode) EncodeFunction (hWnd );
Needencode = SendVideoToBuff (& vd_tx_sdu, buff );
FrameMode = TRUE;
CapPreview (capWnd, FALSE );
CapOverlay (capWnd, FALSE );
CapGrabFrameNoStop (capWnd );
Break;
Case WM_VIDEO_DECODE: // image decoding
Video_Decod_begin = 1;
Play_movie ();
Video_Decod_begin = 0;
Break;
Case WM_COMMAND:
Switch (LOWORD (wParam ))
{
Case IDM_CONNECT: // responds to the call from the other party and connects to the visual phone.
WskConnect (hWnd );
ResumeThread (hThreadTCPRev); // run the TCP/IP receiving thread
ResumeThread (hThreadG7231); // run the speech codec thread
BeginG7231Codec (); // initialize the image acquisition card and start to collect the image
FrameMode = FALSE;
CapWnd = capCreateCaptureWindow (LPSTR) "Capture Window ",
WS_CHILD WS_VISIBLE,
100,100,176,144,
(HWND) hWnd, (int) 0 );
CapSetCallbackOnError (capWnd, (FARPROC) ErrorCallbackProc );
CapSetCallbackOnStatus (capWnd, (FARPROC) StatusCallbackProc );
CapSetCallbackOnFrame (capWnd, (FARPROC) FrameCallbackProc );
CapDriverConnect (capWnd, 0 );
CenterCaptureWindow (hWnd, capWnd );
CapDlgVideoSource (capWnd );
CapDlgVideoFormat (capWnd );
CapDlgVideoCompression (capWnd );
CapGetStatus (capWnd, & capStatus, sizeof (CAPSTATUS ));
StartNewVideoChannel (hWnd, capWnd );
Image = image_one;
FrameMode = TRUE;
CapPreview (capWnd, FALSE );
CapOverlay (capWnd, FALSE );
CapGrabFrameNoStop (capWnd );
Break;
Case IDM_LISTEN: // call the recipient's number
Sock = socket (AF_INET, SOCK_STREAM, 0 );
If (sock = INVALID_SOCKET ){
MessageBox (hWnd, "socket () failed", "Error", MB_ OK );
Closesocket (sock );
Break ;}
If (! FillAddr (hWnd, & local_sin, FALSE) // obtain the TCP/IP address and port number
Break;
EnableMenuItem (GetMenu (hWnd), IDM_LISTEN, MF_GRAYED );
SetWindowText (hWnd, "Waiting for connection ..");
Bind (sock, (struct sockaddr FAR *) & local_sin, sizeof (local_sin );
If (listen (sock, MAX_PENDING_CONNECTS) <0)
{
Sprintf (szBuff, "% d is the error ",
WSAGetLastError (); MessageBox (hWnd, szBuff, "listen (sock) failed ",
MB_ OK );
Break ;}
Tp. hWnd = "hWnd; // start the local TCP/IP receiving thread"
_ Beginthread (AcceptThreadProc, 0, & tp );
ResumeThread (hThreadG7231); // start the thread of local speech codec.
Break;
Case IDM_DISCONNECT: // disconnect the videophone.
CloseG7231Codec ();
SuspendThread (hThreadG7231 );
SuspendThread (hThreadTCPRev );
WSACleanup ();
Init_Video_Decod_Again ();
CapSetCallbackOnError (capWnd, NULL );
CapSetCallbackOnStatus (capWnd, NULL );
InvalidateRect (hWnd, NULL, 1); capSetCallbackOnFrame (capWnd, NULL );
CapSetCallbackOnVideoStream (capWnd, NULL );
CapDriverDisconnect (capWnd );
Init_Wsock (hWnd );
MessageBox (hWnd, "Now closing the Video telephone", "", MB_ OK );
SetDisConnectMenus (hWnd );
SendMessage (hWnd, WM_COMMAND, IDM_LISTEN, NULL );
Break;
Case IDM_EXIT:
CloseG7231Codec ();
SendMessage (hWnd, WM_CLOSE, 0, 0l );
Break; default:
Return (DefWindowProc (hWnd, message, wParam, lParam ));
}
Break;
Case WM_CLOSE:
If (IDOK! = "MessageBox (" hWnd, "OK to close window? ", GszAppName,
MB_ICONQUESTION MB_OKCANCEL) break;
Case WM_DESTROY:
WSACleanup ();
CloseG7231Codec ();
TerminateThread (hThreadG7231, 0 );
TerminateThread (hThreadTCPRev, 0 );
CapSetCallbackOnError (capWnd, NULL );
CapSetCallbackOnStatus (capWnd, NULL );
CapSetCallbackOnFrame (capWnd, NULL );
CapSetCallbackOnVideoStream (capWnd, NULL );
CapDriverDisconnect (capWnd );
FreeAll ();
PostQuitMessage (0 );
Break;
Default:/* Passes it on if unproccessed */
Return (DefWindowProc (hWnd, message, wParam, lParam ));
}
Return (0 );
}

// Caller TCP/IP receiving thread
Dword winapi AcceptThreadProc (PTHREADPACK ptp)
{
SOCKADDR_IN acc_sin;/* Accept socket address internet style */
Int acc_sin_len;/* Accept socket address length */
Int status;
Acc_sin_len = "sizeof (acc_sin );"
// Call the blocking function accept until the remote response ends.
Sock = "accept (" sock, (struct sockaddr FAR *) & acc_sin, (int FAR *) & acc_sin_len );
If (sock <0)
{
Sprintf (szBuff, "% d is the error", WSAGetLastError ());
MessageBox (ptp-> hWnd, szBuff, "accept (sock) failed", MB_ OK );
Return (1 );
}
SetConnectMenus (ptp-> hWnd); // remote pick-up and visual phone connection
BeginG7231Codec ();
While (1)
{
Beg1:
Status = recv (SOCKET) sock, r_mux_buf, MY_MSG_LENGTH, NO_FLAGS_SET );
If (status = SOCKET_ERROR ){
Status = WSAGetLastError ();
If (status = 10054 ){
MessageBox (ptp-> hWnd, "the other party hangs up", "Indication", MB_ OK );
SendMessage (ptp-> hWnd, WM_COMMAND, IDM_DISCONNECT, NULL );
_ Endthread ();
Return (1 );
}
Goto beg1;
}
If (status ){
R_mux_buf [status] = '\ 0 ';
If (r_mux_buf_filled = 1)
R_mux_buf_overwrite = 1;
Else
R_mux_buf_filled = 1;
R_mux_buf_length = status;
}
Else
{
MessageBox (hWnd, "Connection broken", "Error", MB_ OK );
SendMessage (ptp-> hWnd, WM_COMMAND, IDM_DISCONNECT, NULL );
_ Endthread ();
Return (2 );
}
Demux (); // decodes the line code stream H.223
}
Return (0 );
}

// The called party's TCP/IP receiving thread
Dword winapi AcceptThreadProcRev (PTHREADPACK ptp)
{
Int status;
While (1)
{
Beg2:
Status = recv (SOCKET) sock, r_mux_buf, MY_MSG_LENGTH, NO_FLAGS_SET );
If (status = SOCKET_ERROR)
{
Status = WSAGetLastError ();
If (status = 10054)
{
MessageBox (ptp-> hWnd, "the other party hangs up", "Indication", MB_ OK );
SendMessage (ptp-> hWnd, WM_COMMAND, IDM_DISCONNECT, NULL );
Return (1 );
}
Goto beg2;
}
If (status)
{
R_mux_buf [status] = '\ 0 ';
If (r_mux_buf_filled = 1)
R_mux_buf_overwrite = 1;
Else
R_mux_buf_filled = 1;
R_mux_buf_length = status;
}
Else
{
MessageBox (hWnd, "Connection broken", "Error", MB_ OK );
SendMessage (ptp-> hWnd, WM_COMMAND, IDM_DISCONNECT, NULL );
Return (2 );
}
Demux ();
}/* While (forever )*/
Return (0 );
}

// Speech codec thread
Dword winapi G723Proc (G7231DATA * data)
{
Int I, len;
Audio_tx_pduad_tx_pdu;
Unsigned char mux [MAX_MUX_PDU_SIZE];
Do
{
Len = 0;
// Check whether there is a local voice. The image code stream must be transmitted.
I = DetectAudioVideoData ();
Switch (I)
{
Case AUDIO_ONLY: // only the voice code stream
AL2_CRC_coder (& ad_tx_sdu, & ad_tx_pdu );
// H.223 package
Len = AL2_To_MUX (& ad_tx_pdu, mux );
Break;
Case VIDEO_ONLY: // only the image code stream
SDU_To_PDU (& vd_tx_sdu, & vd_tx_pdu );
Tx_AL3_ I _PDU (& vd_tx_pdu, & bs, 1); // package H.223
Len = AL3_To_MUX (& vd_tx_pdu, mux );
Break;
Case AUDIO_VIDEO: // speech and image code streams
AL2_CRC_coder (& ad_tx_sdu, & ad_tx_pdu );
SDU_To_PDU (& vd_tx_sdu, & vd_tx_pdu );
Tx_AL3_ I _PDU (& vd_tx_pdu, & bs, 1 );
// H.223 package
Len = AL2_AL3_To_MUX (& ad_tx_pdu, & vd_tx_pdu, mux );
Break;
Case NO_AUDIO_VIDEO: // No-code stream needs to be transmitted at the moment
Break;
}
// TCP/IP sending code stream
If (len! = 0)
Send (SOCKET) sock, mux, len, 0 );
// Whether the code stream to be decoded is received. If yes, the decoder is called.
PutVideoStreamToDecod ();
}
While (1 );
Return (0 );
}

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.