6

演示如何使用 MFC 创建线程的示例代码将线程函数声明为静态和__cdecl. 为什么需要后者?Boost 线程不关心这个约定,所以它只是一个不合时宜的东西吗?

例如(MFC):

static __cdecl UINT MyFunc(LPVOID pParam)
{
...
}

CWinThread* pThread = AfxBeginThread(MyFunc, ...);

而提升:

static void func()
{
...
}

boost::thread t;
t.create(&func);

(代码示例可能不是 100% 正确,因为我离 IDE 还很远)。

__cdecl 的意义何在?创建线程时它有什么帮助?

4

5 回答 5

4

__cdecl 告诉编译器使用 C 调用约定(与 stdcall、fastcall 或您的编译器支持的任何其他调用约定相反)。我相信,VC++ 默认使用 stdcall。

调用约定会影响诸如参数如何被压入堆栈(或寄存器,在 fastcall 的情况下)以及谁将参数从堆栈中弹出(调用者或被调用者)之类的事情。

在升压的情况下。我相信它使用模板专业化来找出适当的函数类型和调用约定。

于 2008-10-04T14:11:59.527 回答
4

查看原型AfxBeginThread()

CWinThread* AfxBeginThread(
   AFX_THREADPROC pfnThreadProc,
   LPVOID pParam,
   int nPriority = THREAD_PRIORITY_NORMAL,
   UINT nStackSize = 0,
   DWORD dwCreateFlags = 0,
   LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL 
);

AFX_THREADPROCUINT(AFX_CDECL*)(LPVOID). 当您将函数传递给 时AfxBeginThread(),它必须匹配该原型,包括调用约定。

__cdecl__stdcall(以及__fastcall和)上的 MSDN 页面__thiscall解释了每种调用约定的优缺点。

构造boost::thread函数使用模板允许您传递函数指针或可调用函数对象,因此它没有与 MFC 相同的限制。

于 2008-10-04T14:15:54.480 回答
1

因为您的线程将由为您管理它的运行时函数调用,并且该函数期望它是这样的。Boost 以不同的方式设计了它。

在线程函数的开头放置一个断点并在调用它时查看堆栈,您将看到调用您的运行时函数。

于 2008-10-04T14:10:53.850 回答
1

默认情况下,C/C++ 编译器使用 C 调用约定(首先将最右边的参数推入堆栈),因为它允许使用具有可变参数编号的函数作为 printf。

Pascal 调用约定(又名“fastcall”)首先推送最左边的参数。尽管您需要使用一些技巧,但这会更快,尽管您可能会使用简单的可变参数函数(我在某处读到它们仍然可能,但您需要使用一些技巧)。

由于使用 Pascal 约定带来的速度,Win32 和 MacOS API 在默认情况下都使用该调用约定,除非在某些情况下。

如果该函数只有一个参数,理论上使用任一调用约定都是合法的,尽管编译器可能会强制使用相同的调用约定来避免任何问题。

boost 库的设计着眼于可移植性,因此它们应该不知道特定编译器正在使用哪种调用者约定。

于 2008-10-04T16:17:53.953 回答
1

真正的答案与 windows 如何在内部调用线程 proc 例程有关,它期望函数遵守特定的调用约定,在这种情况下是宏 WINAPI,根据我的系统定义为:

#define WINAPI      __stdcall

这意味着被调用的函数负责清理堆栈。boost::thread 能够支持任意函数的原因是它传递了一个指向函数对象的指针,该指针用于调用 thread::create 函数到 CreateThread。与线程关联的 threadproc 只是简单地调用函数对象上的 operator()。

因此,MFC 需要 __cdecl 的原因与它在内部调用传递给 AfxBeginThread 调用的函数的方式有关。除非他们计划允许可变参数,否则没有充分的理由这样做......

于 2008-10-17T22:19:50.600 回答