多线程问题___C++11与WindowsAPI的多线程

1、多线程的发展

早期1998 C++标准版不承认线程的存在,内存模型也没有被正式定义,所以对于1998 C++标准,你没办法在缺少编译器相关扩展的情况下编写多线程应用程序。
早期只能借助编译器厂商提供的平台相关的扩展多线程支持API如WindowsAPI,这就导致了多线程应用的可移植性差。
直到C++11发布将多线程支持纳入C++standard库。

2、并发与并行

并发:同一时间段内可以交替处理多个操作,强调同一时段内交替发生。
在这里插入图片描述
并行:同一时刻内同时处理多个操作,强调同一时刻点同时发生。
在这里插入图片描述

3、什么是线程安全?

当多个线程访问某个方法时,不管你通过怎样的调用方式、或者说这些线程如何交替地执行,我们在主程序中不需要去做任何的同步,这个类的结果行为都是我们设想的正确行为,那么我们就可以说这个类是线程安全的。
既然是线程安全问题,那么毫无疑问,所有的隐患都是在多个线程访问的情况下产生的,也就是我们要确保在多条线程访问的时候,我们的程序还能按照我们预期的行为去执行。
线程安全是另一个值得深究的问题。。。。

4、WindowsAPI

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
    )

在MFC中也可以直接用
AfxBeginThread();
MFC提供了两个重载版的AfxBeginThread,一个用于用户界面线程,另一个用于工作者线程,区别在于用户界面线程能处理消息响应,而工作者线程不能。其原型和过程:

用户界面线程的AfxBeginThread原型如下:

CWinThread* AFXAPI AfxBeginThread
(
    CRuntimeClass* pThreadClass,
    int nPriority, 
    UINT nStackSize, 
    DWORD dwCreateFlags,
    LPSECURITY_ATTRIBUTES lpSecurityAttrs

)

其中:
参数1是从CWinThread派生的RUNTIME_CLASS类;
参数2指定线程优先级,如果为0,则与创建该线程的线程相同;
参数3指定线程的堆栈大小,如果为0,则与创建该线程的线程相同;
参数4是一个创建标识,如果是CREATE_SUSPENDED,则在悬挂状态创建线程,在线程创建后线程挂起,否则线程在创建后开始线程的执行。
参数5表示线程的安全属性。

工作者线程的AfxBeginThread ,原型如下

CWinThread* AFXAPI AfxBeginThread
(
    AFX_THREADPROC pfnThreadProc, 
    LPVOID pParam,
    int nPriority, 
    UINT nStackSize, 
    DWORD dwCreateFlags,
    LPSECURITY_ATTRIBUTES lpSecurityAttrs
)

其中:
参数1 线程的入口函数,声明一定要如下: UINT MyThreadFunction( LPVOID pParam );
参数2 传递入线程的参数,类型为LPVOID,所以我们可以传递一个结构体入线程.
参数3、4、5分别指定线程的优先级、堆栈大小、创建标识、安全属性,含义同用户界面线程。

AfxBeginThread除前面两个参数外,后面几个都是默认参数,可以省略。前两个参数中,第一个是线程函数的指针,第二个是传递给这个函数的参数。
实际中我们经常这样用:

AfxBeginThread(ThreadProc,this);

把this传过去,线程函数就可以使用和操作类的成员了。

例如:
线程函数声明为全局函数:

UINT _cdecl thread_begin(LPVOID pParam)
{
    
    
	//将传入的参数转回来
	CSDK_TestDlg *dlg = (CSDK_TestDlg*)pParam;
	
	VideoCapture videocap;
	Mat src,dst;
	videocap.open(0);
	while (true)
	{
    
    
		videocap.read(src);
		int NewWidth = src.cols;
		int NewHeight = src.rows;
		double XScale = double(dlg->rect.right - dlg->rect.left) / double(src.cols);
		double YScale = double(dlg->rect.bottom - dlg->rect.top) / double(src.rows);
		NewWidth = src.cols * XScale;
		NewHeight = src.rows * YScale;
		cv::Size NewSize(NewWidth, NewHeight);
		cv::resize(src, dst, NewSize);
		cv::imshow("view", dst);
		waitKey(20);
	}
}

在CSDK_TestDlg类里面使用

	AfxBeginThread(thread_begin, this, 0, THREAD_PRIORITY_NORMAL, 0);
	//this指针指向的是当前Dlg的句柄
	

注意:
线程函数是静态类成员函数或者全局函数。
线程函数的声明必须为: UINT MyThreadFunction( LPVOID pParam );

5、std库thread的简单操作

多线程编程与普通编程唯一真正的区别在于某些函数可以并发运行,所以你需要确保共享数据的并发访问是安全的。为了并发地运行函数,必须使用特定的函数以及对象来管理各个线程。

前面一篇文章提到过C++11的多线程问题,代码我就直接复制粘贴以前的了;

#include <iostream>
#include <utility>
#include <thread>
#include <chrono>
 
void f1(int n)
{
    
    
    for (int i = 0; i < 5; ++i) {
    
    
        std::cout << "Thread 1 executing\n";
        ++n;
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
}
 
void f2(int& n)
{
    
    
    for (int i = 0; i < 5; ++i) {
    
    
        std::cout << "Thread 2 executing\n";
        ++n;
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
}
 
class foo
{
    
    
public:
    void bar()
    {
    
    
        for (int i = 0; i < 5; ++i) {
    
    
            std::cout << "Thread 3 executing\n";
            ++n;
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
    }
    int n = 0;
};
 
class baz
{
    
    
public:
    void operator()()
    {
    
    
        for (int i = 0; i < 5; ++i) {
    
    
            std::cout << "Thread 4 executing\n";
            ++n;
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
    }
    int n = 0;
};
 
int main()
{
    
    
    int n = 0;
    foo f;
    baz b;
    std::thread t1; // t1 is not a thread
    std::thread t2(f1, n + 1); // 值传递
    std::thread t3(f2, std::ref(n)); // 引用传递
    std::thread t4(std::move(t3)); // t4运行t3,t3不再运行
    std::thread t5(&foo::bar, &f); // t5 在对象f上运行foo类的bar()函数(函数地址跟对象地址都传进去了)
    std::thread t6(b); 
    // t6 在对象 b 的一个副本上运行baz类的operator()()函数
   //重载运算符(operator())
    t2.join();
    t4.join();
    t5.join();
    t6.join();
    std::cout << "Final value of n is " << n << '\n';
    std::cout << "Final value of f.n (foo::n) is " << f.n << '\n';
    std::cout << "Final value of b.n (baz::n) is " << b.n << '\n';
}

猜你喜欢

转载自blog.csdn.net/qq_40595787/article/details/121806777
今日推荐