C++中的线程封装与Win32 API的整合

在面向对象编程(OOP)中,C++以其封装性、继承性和多态性而著称。然而,大多数操作系统并不是为C++设计的,它们通常使用非面向对象的方法实现,这导致在C++中封装某些平台依赖资源时会遇到一些挑战。本文将探讨如何在C++中封装Win32线程,使之符合面向对象编程的原则。

动机

面向对象语言如C++的优势在于其能够封装对象的表示和实现,使得编程更加关注于接口级别而非函数级别。然而,大多数操作系统并没有考虑到C++的特性,它们通常使用非OO的方法实现。因此,封装某些平台依赖资源,如线程,可能会变得复杂。本文的方法涵盖了Win32线程的封装。

Win32线程

在Win32中,创建同一进程中的另一个线程需要使用几个API函数来处理线程。然而,它们是C而非C++ API。可以很容易地注意到C的编程习惯,如回调函数、从void*转换到其他类型等。让看看在Win32中创建线程的API函数CreateThread的原型:

HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId );

其中,lpStartAddress是一个指向将在新线程中运行的回调函数的指针,lpParameter是传递给新线程的void*类型的参数。

然而,传递指向回调函数的指针并不符合OOP的精神,并且如果想要在类中封装线程,它就像一个严重的障碍。传递给CreateThread的回调函数看起来像这样:

DWORD WINAPI ThreadProc( LPVOID lpParameter );

会发现这个原型会阻止将这个回调函数作为类的成员,因为成员函数会传递一个隐藏的参数:this。那么该怎么办呢?失败了吗?还没有。不能使用成员函数,已经看到了为什么,但是类有静态方法,它们对所有对象都有单一实例。它们与类而不是对象相关联。这就是为什么它们不会作为参数传递this。因此,静态函数成为回调函数的一个有趣候选。但是有一个小问题,如果线程在静态方法中,那么不管有多少个该类的对象,都只会有一个线程,因为静态方法每个类只有一个实例。这不是想要的。希望工作线程是一个成员方法,易于被子类覆盖,所有这些工作方式对客户都是透明的。能做到吗?

是的,能做到。如果看ThreadProc,会注意到它可以传递一个void*参数。没有什么可以阻止发送(void*)this,在ThreadProc中只是调用工作方法,现在有了this指针。这样做的代码看起来像这样:

// 这里创建线程 HANDLE CThread::CreateThread() { return ::CreateThread((NULL, 0, (unsigned long (__stdcall *)(void *))this->runProcess, (void *)this, 0, NULL); } // 静态方法 int CThread::runProcess(void* pThis) { return ((CThread*)(pThis))->Process(); } // 工作方法,虚拟的,可覆盖的 int CThread::Process() { // 将在另一个线程中工作 }

到目前为止,一切都很好。设法提供了线程机制的封装,用户只需要实现他们自己的Process,然后调用CreateThread成员函数。它甚至更容易提供可重用性,因为用户可以简单地继承上面定义的类CThread,然后实现Process,然后调用CreateThread,他们就有了一个线程,就是那么简单。但是这里有一个需要注意的小问题:

假设有一个CThread的子类,名为CMyThread。在CreateThread中将this(CMyThread*类型)转换为void*并传递给runProcess,在runProcess中将其重新转换为CThread。C++标准规定,如果将类型X*转换为void*,那么只有转换回相同类型X*是允许的。其他转换会导致未定义行为。这意味着做错了什么。该如何修复呢?嗯,用一个小的变通方法。

struct workAround { CThread* this_thread; }; // 传递一个workAround结构而不是this HANDLE CThread::CreateThread() { workAround* wA = new workAround; wA->this_thread = this; return ::CreateThread((NULL, 0, (unsigned long (__stdcall *)(void *))this->runProcess, (void *)wA, 0, NULL); } // 静态方法 int CThread::runProcess(void* pThis) { workAround* wA = (workAround*)pThis; // 这将调用适当的方法,因为Process是虚拟方法 CThread* thread = wA->this_thread; delete wA; return thread->Process(); }

这次没问题了,因为在转换到和从相同的类型(struct workAround)。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485