多线程的一点思考

本文探讨了使用CreateThread创建线程时线程入口函数为何采用__stdcall调用约定,并解释了_beginthread函数与CreateThread的关系及其内部实现细节。此外,还通过一个示例程序展示了线程资源管理的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.使用CreateThread创建线程,其中第三个参数是LPTHREAD_START_ROUTINE lpStartAddress,

而LPTHREAD_START_ROUTINE被定义为

typedef DWORD (WINAPI *PTHREAD_START_ROUTINE)(
    LPVOID lpThreadParameter
    );

#define WINAPI      __stdcall

 

也就是线程入口函数应该是__stdcall方式。

为什么?(我的想法,不一定正确)

因为线程入口函数和一般的函数调用不一样,在这里调用者(创建新线程的函数)并不知道被调用者(新线程入口函数)什么时候返回,

调用者也就没法在被调用者调用结束后清理参数栈,所以必须有被调用者自己在退出时清理参数栈。

所以为__stdcall方式。

 

2. 编写C/C++程序,创建线程应使用_beginthread函数。

而_beginthread函数的第一个参数是void (__cdecl * _StartAddress) (void *),需要__cdecl方式的函数。

这是怎么回事?

 

(1) 其实_beginthread内部调用了CreateThread,它是这样调用的:

ptd->_initaddr = (void *) initialcode;
ptd->_initarg = argument;

CreateThread( NULL,
                            stacksize,
                            _threadstart,
                            (LPVOID)ptd,
                            CREATE_SUSPENDED,
                            (LPDWORD)&(ptd->_tid) ))

也就是说,他创建的线程入口函数是_threadstart,而_threadstart原型:

static unsigned long WINAPI _threadstart(void *); 也就是__stdcall方式。

 

(2)_threadstart内部再调用_callthreadstart,_callthreadstart原型:

static void _callthreadstart(void);

 

(3)最后由_callthreadstart调用我们的线程函数

__try
    {
        ( (void(__CLRCALL_OR_CDECL *)(void *))(((_ptiddata)ptd)->_initaddr) )
            ( ((_ptiddata)ptd)->_initarg );

        _endthread();
    }

在线程结束后,它会调用_endthread,清理C/C++运行期资源。

 

3.在《Windows核心编程》中说终止进程的运行的最好方式是主线程的进入点函数返回 ,这样能保证所有线程资源能够得到正确清除。

实际上使用下面这个例子:

 

程序的运行结果如下:

A::A(global)
A::A(thread_func)
A::~A(global)
可见线程thread_func中的local_a析构函数未被调用,仍然可能有资源泄漏。可见,每一个线程函数也都要小心,要确保在主线程结束之前能正常退出,而主线程也要能保证所有其他线程正常退出后再退出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值