C++ multithreaded programming simple example

  • 2020-04-02 03:01:22
  • OfStack

C++ itself does not provide any multi-threading mechanism, but in Windows, we can call the SDK win32 API to write multi-threaded procedures, the following is a simple talk:

A function that creates a thread


HANDLE CreateThread(
    LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD
    SIZE_T dwStackSize,                       // initial stack size
    LPTHREAD_START_ROUTINE lpStartAddress,    // thread function
    LPVOID lpParameter,                       // thread argument
    DWORD dwCreationFlags,                    // creation option
    LPDWORD lpThreadId                        // thread identifier
);

Here we use only the third and fourth arguments, the third passing the address of a function and the new thread to specify, and the fourth argument is the parameter pointer to the new thread.

Eg1:


#include <iostream>
#include <windows.h>
using namespace std;
DWORD WINAPI Fun(LPVOID lpParamter)
{
      while(1) { cout<<"Fun display!"<<endl; }
}
int main()
{
    HANDLE hThread = CreateThread(NULL, 0, Fun, NULL, 0, NULL);
    CloseHandle(hThread);
    while(1) { cout<<"main display!"<<endl;  }
    return 0;
}

We can see that the main thread (the main function) and our own thread (the Fun function) execute alternately at random, but the two threads output too fast for us to see, so we can use the function


VOID Sleep(
    DWORD dwMilliseconds   // sleep time
);

To pause the thread execution. DwMilliseconds is one thousandth of a second, so

Sleep (1000);

Pause for 1 second

Eg2:


#include <iostream>
#include <windows.h>
using namespace std;
DWORD WINAPI Fun(LPVOID lpParamter)
{   
      while(1) { cout<<"Fun display!"<<endl; Sleep(1000);}
}
int main()
{
      HANDLE hThread = CreateThread(NULL, 0, Fun, NULL, 0, NULL);
      CloseHandle(hThread);
      while(1) { cout<<"main display!"<<endl;  Sleep(2000);}
      return 0;
}

Executing the above code, this time we can clearly see the interlaced output of Fun display on the screen! And the main display! , we found that these two functions is, indeed, run concurrently, careful readers may find that our procedure is when the Fun function and the main function will output a newline after output, but it's true that we are seeing is sometimes program output line feed, sometimes no output line breaks, and sometimes even two output line. What's going on here? Now let's change the program and see:

Eg3:


#include <iostream>
#include <windows.h>
using namespace std;
DWORD WINAPI Fun(LPVOID lpParamter)
{
      while(1) { cout<<"Fun display!n"; Sleep(1000);}
}
int main()
{
      HANDLE hThread = CreateThread(NULL, 0, Fun, NULL, 0, NULL);
      CloseHandle(hThread);
      while(1) { cout<<"main display!n";  Sleep(2000);}
      return 0;
}

We run the program again, and we find that at this point, as we expected, the output is correct and the format is correct. Now let me explain why our program didn't work correctly. Multithreaded programs run concurrently. If resources are Shared between multiple threads, there is no guarantee that they will be used correctly, because resources are not exclusive.

Eg4:

Add a resource int a = 3

There's a thread function called selfAdd() that makes a += a;

I have another thread function selfSub() that makes a -= a;

We assume that the above two threads are concurrent lines, if selfAdd at the time of execution, our purpose is to want to make a programming 6, but at the moment selfSub had a chance to run, so become a 0, wait until selfAdd to execute the opportunity, a + = a, this is indeed a 0, however, is not as we expected to 6, we returned to the front EG2, here, we can see the screen as a resource, this resource is Shared by the two threads, added to the output when the Fun function Fun display! After that, we are about to output endl (which means empty the buffer and wrap it, so we don't have to understand anything here), but the main function does get a chance to run, and the Fun function gives up the CPU to the main function before it has time to wrap it, and the main function is directly in the Fun display! Main display! , as to why sometimes the program will output two consecutive lines, the reader can use the same analysis method to analyze, here I will not say much, leave the reader to think.

So why do we change eg2 to eg3 to work correctly? The reason is that although multiple threads are running concurrently, there are some operations that must be done in one go without interruption, so we can see that eg2 and eg3 run differently.

So, is the eg2 code something we can't get right? The answer is of course no, but I'll show you how to make the eg2 code work correctly. This involves synchronization of multiple threads. The solution to this problem is to allow only one thread to own an exclusive share of the Shared resource.


HANDLE CreateMutex(
    LPSECURITY_ATTRIBUTES lpMutexAttributes,  // SD
    BOOL bInitialOwner,                       // initial owner
    LPCTSTR lpName                            // object name
 );

This function is used to create an exclusive resource. The first parameter is not used and can be set to NULL, the second parameter specifies whether the resource was originally owned by the process that created it, and the third parameter specifies the name of the resource.


HANDLE hMutex = CreateMutex(NULL,TRUE,"screen");

This statement creates a resource named screen and belongs to the process that created it


BOOL ReleaseMutex(
    HANDLE hMutex   // handle to mutex
  );

This function is used to release an exclusive resource. Once the process releases the resource, it no longer belongs to it. The function for the requested resource is as follows


DWORD WaitForSingleObject(
    HANDLE hHandle,        // handle to object
    DWORD dwMilliseconds   // time-out interval
  );

The first parameter specifies the handle of the applied resource, and the second parameter is generally specified as INFINITE, which means that if the resource is not applied, it will wait for the resource; if it is specified as 0, it means that it will return once the resource is not available, or it can specify how long it will wait to return, in thousandths of a second. Well, it's time for us to fix eg2. We can make some changes to eg2 as follows

Eg5:


#include <iostream>
#include <windows.h>
using namespace std;
HANDLE hMutex;
DWORD WINAPI Fun(LPVOID lpParamter)
{
       while(1) {
                 WaitForSingleObject(hMutex, INFINITE);
                 cout<<"Fun display!"<<endl;
                 Sleep(1000);
                 ReleaseMutex(hMutex);
        }
  }
int main()
{
      HANDLE hThread = CreateThread(NULL, 0, Fun, NULL, 0, NULL);
      hMutex = CreateMutex(NULL, FALSE, "screen");
      CloseHandle(hThread);
      while(1) {
               WaitForSingleObject(hMutex, INFINITE);
               cout<<"main display!"<<endl; 
               Sleep(2000);
               ReleaseMutex(hMutex);
      }
      return 0;
}

Run the code as we expect the output to be.

The above is all the content of this article, I hope you can enjoy it.


Related articles: