C++多线程入门

       多线程在编程中有相当重要的地位,我们在实际开发时或者找工作面试时总能遇到多线程的问题,对多线程的理解程度从一个侧面反映了程序员的编程水平。

    其实C++语言本身并没有提供多线程机制(当然目前C++ 11新特性中,已经可以使用std::thread来创建线程了,因为还没有系统地了解过,所以这里不提了。),但Windows系统为我们提供了相关API,我们可以使用他们来进行多线程编程。创建线程的API函数:

HANDLE CreateThread(
    LPSECURITY_ATTRIBUTES lpThreadAttributes,//SD:线程安全相关的属性,常置为NULL
    SIZE_T dwStackSize,//initialstacksize:新线程的初始化栈的大小,可设置为0
    LPTHREAD_START_ROUTINE lpStartAddress,//threadfunction:被线程执行的回调函数,也称为线程函数
    LPVOID lpParameter,//threadargument:传入线程函数的参数,不需传递参数时为NULL
    DWORD dwCreationFlags,//creationoption:控制线程创建的标志
    LPDWORD lpThreadId//threadidentifier:传出参数,用于获得线程ID,如果为NULL则不返回线程ID
    )
BOOL WINAPI CloseHandle(HANDLE hObject); //关闭一个被打开的对象句柄


exp1:

#include "stdafx.h"
#include <iostream>   
#include <windows.h>   
using namespace std;

DWORD WINAPI Fun(LPVOID lpParamter)
{
    for (int i = 0; i < 10; i++)
    {
        //cout << "A Thread Fun Display!" << endl;
        cout << "A Thread Fun Display!\n";
        Sleep(200);
    }
        
    return 0L;
}

int main()
{
    HANDLE hThread = CreateThread(NULL, 0, Fun, NULL, 0, NULL);
    CloseHandle(hThread);
    for (int i = 0; i < 10; i++)
    {
        //cout << "Main Thread Display!" << endl;//不是一气呵成,命令换行endl是一个单独的
        cout << "Main Thread Display!\n";//一气呵成
        Sleep(500);
    }
        
    return 0;
}



     1. 这时候,正如我们预期的,正确地输出了我们想要输出的内容并且格式也是正确的。在这里,我们可以把屏幕看成是一个资源,这个资源被两个线程所共用,加入当Fun函数输出了Fun Display!后,将要输出endl(也就是清空缓冲区并换行,在这里我们可以不用理解什么是缓冲区),但此时,main函数却得到了运行的机会,此时Fun函数还没有来得及输出换行(时间片用完),就把CPU让给了main函数,而这时main函数就直接在Fun Display!后输出Main Display!。


 2.另一种情况就是“输出两个换行”,这种情况就是比如输出Main Display!并输出endl后,时间片用完,轮到子线程占用CPU,子进程上一次时间片用完时停在了Fun Display!,下一次时间片过来时,刚好开始输出endl,此时就会“输出两个换行”。


 3.那么为什么我们把实例2改成实例3就可以正确的运行呢?原因在于,多个线程虽然是并发运行的,但是有一些操作(比如输出一整段内容)是必须一气呵成的,不允许打断的,所以我们看到实例2和实例3的运行结果是不一样的。它们之间的差异就是少了endl,而多了一个换行符\n。

exp2:

#include "stdafx.h"
#include <iostream>
#include <windows.h>
#include <mmsystem.h>  
#pragma comment(lib, "winmm.lib") 
using namespace std;
 
DWORD WINAPI Fun(LPVOID lpParamter)
{
     PlaySound(TEXT("D://vvooy//em//ALARM1.WAV"), NULL, SND_FILENAME |SND_SYNC| SND_LOOP);//Absolute path Sleep(1000);
	 return 0;
}
  
DWORD WINAPI Fun1(LPVOID lpParamter)
{
     PlaySound(TEXT("D://vvooy//em//ALARM1.WAV"), NULL, SND_FILENAME |SND_SYNC| SND_LOOP);//Absolute path Sleep(1000);
	
	 return 0;
}
int main()
{
    
	  int s[]={1,2,3,4,5,6};
	  for(int i=0;i<6;i++)
	  {
		  cout<<"s["<<i<<"]="<<s[i]<<endl;
		  if(s[i]<4)
		  {
		    HANDLE hThread = CreateThread(NULL, 0, Fun, NULL, 0, NULL);

			CloseHandle(hThread);
			cout<<"Fun display!\n"; //Sleep(1000);
		  }
		  else
			{
				HANDLE hThread = CreateThread(NULL, 0, Fun1, NULL, 0, NULL);
				CloseHandle(hThread);
		   cout<<"Fun1 display!\n"; //Sleep(1000);
		  }
		  Sleep(3000);
	  }
      return 0;
}
从这个小例子中明显看出:主函数与Fun函数同时运行,因为主函数和Fun函数执行内容的速度不同,所以主函数延迟3s是为了等待Fun函数;

exp3:

      那么,是不是exp1被注释掉的"<<endl"的代码不能正确运行吗?答案当然是否定的,下面我就来讲一下怎样才能让实例2的代码可以正确运行。这涉及到多线程的同步问题。对于一个资源被多个线程共用会导致程序的混乱,我们的解决方法是只允许一个线程拥有对共享资源的独占,这里我们用互斥量(Mutex)来进行线程同步。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值