设为首页 加入收藏

TOP

C++拾遗--多线程:多线程的引入
2015-07-20 17:15:19 来源: 作者: 【 】 浏览:2
Tags:拾遗 线程 引入

C++拾遗--多线程:多线程的引入

前言

多线程是编程中的一个重要内容。多核时代使多线程成为一种可能,显然,一件事情多个人干,效率一定会提升。下面来看下C语言中是如何使用多线程的。

正文

1.CreateThread

先来看一个实例

?

#define _CRT_SECURE_NO_WARNINGS
#include 
  
   
#include 
   
     #include 
    
      #include <
     windows.h> DWORD WINAPI run(void *p) { char *mess = (char*)p; printf(线程%d,弹窗 , GetCurrentThreadId()); char threadId[20]; sprintf(threadId, 线程%d, GetCurrentThreadId()); MessageBoxA(0, mess, threadId, 0); return 0; } int main(void) { printf(******C语言多线程演示***by David*** ); char *mess[] = { 123, 456, 789 }; HANDLE handles[3]; for (int i = 0; i < sizeof(mess) / sizeof(*mess); i++) { handles[i] = CreateThread(NULL, 0, run, mess[i], 0, NULL); } WaitForMultipleObjects(3, handles, 1, INFINITE); return 0; }
    
   
  
运行

?

\

?

异步弹出了三个窗口,并打印了各自的线程号。若是有没看懂的地方,下面有详细解释:

1.handle是句柄,在windows中用句柄来标识对象。本质很简单 typedef void * HANDLE;

2.CreateThread()用来创建线程。原型

HANDLE WINAPI CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, //内核对象的安全属性
SIZE_T dwStackSize, //线程栈大小
LPTHREAD_START_ROUTINE lpStartAddress, //线程函数地址
LPVOID lpParameter, //传给线程函数的参数
DWORD dwCreationFlags, //控制位
LPDWORD lpThreadId //获取线程id
);

参数解释:

第一个参数是线程内核对象的安全属性,一般传入NULL表示使用默认设置。

第二个参数是线程栈空间的大小。传入0表示使用默认大小(1MB)。

第三个参数是新线程所执行的线程函数地址,多个线程可以使用同一个函数地址。

第四个参数是传给线程函数的参数。typedef void * LPVOID

第五个参数是用来控制线程的创建,0表示创建后立即执行。

第六个参数是传出参数,用来获得线程的id。显然,传入NULL,表示调用者并不想知道线程的id。

返回值:线程句柄

3.线程函数的声明。#define WINAPI __stdcall (vs2013)typedef unsigned long DWORD。其中,__stdcall是指C/CPP中函数的调用方式。主要有两点:1.实参从右向左入栈。2.调用者负责清空参数栈。

4.线程等待函数

DWORD WINAPI WaitForMultipleObjects(
DWORD nCount, //内核对象的个数
CONST HANDLE *lpHandles, //句柄数组的地址
BOOL bWaitAll, //是否等待所有
DWORD dwMilliseconds //等待的最大时间,单位毫秒,INFINITE表示无限等待
);

函数功能:让线程进入等待转态,直到条件触发。内核对象在运行期间处于未触发的状态,直到执行结束。

5.线程函数类型是

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

更简洁的是

typedef unsigned long (__stdcall *pfun)(void*);

?

2._beginthreadex

函数原型

uintptr_t __cdecl _beginthreadex(
void* _Security,
unsigned _StackSize,
_beginthreadex_proc_type _StartAddress,
void* _ArgList,
unsigned _InitFlag,
unsigned* _ThrdAddr
);

它的参数类型和CreateThread基本一致,只是线程函数类型稍有不同。线程函数类型是

typedef unsigned (__stdcall *_beginthreadex_proc_type)(void*);

?

3._beginthread

CreateThread的调用过于复杂,下面我们玩儿个简单的,我们用多个线程打印 Hello World

?

#include 
  
   
#include 
   
     #include 
    
      #include 
     
       void hello(void *p) { printf(线程%d, say Hello World , GetCurrentThreadId()); } int main() { printf(******C语言多线程演示***by David*** ); HANDLE handles[5]; for (int i = 0; i < 5; i++) { handles[i] = _beginthread(hello, 0, NULL); } WaitForMultipleObjects(5, handles, 1, INFINITE); getchar(); return 0; }
     
    
   
  
运行

?

\
?

_beginthread的原型

uintptr_t _beginthread(
_beginthread_proc_type _StartAddress, //线程函数的地址
unsigned _StackSize, //线程栈的大小
void* _ArgList //线程函数的参数
);

函数功能:使用指定线程函数创建线程,并返回线程句柄。

几点解释:

1.typedef unsigned int * uintptr_t;

2.typedef void(__cdecl *_beginthread_proc_type)(void*); _beginthread_proc_type就是一函数指针类型,我们提供的线程函数应该如此设计:只有一个参数,类型为void*,且返回值类型是void。

?

CreateThread和_beginthread的使用说明:

?

从函数参数可以看出,CreateThread用于对所创建的线程进行精细控制。在很多参数处于默认设置下,建议使用参数简单的_beginthread。两者所需的线程函数类型不同。

?

?

总结

?

使用多线程,就要先写好线程函数,然后调用相关函数创建线程即可。由于_beginthread传参简单,一般情况下,使用_beginthread创建多线程。


?

?

?

?

?

】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇POJ 1375 Intervals 下一篇素数间隔 UVa1644

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容:

·Redis on AWS:Elast (2025-12-27 04:19:30)
·在 Spring Boot 项目 (2025-12-27 04:19:27)
·使用华为开发者空间 (2025-12-27 04:19:24)
·Getting Started wit (2025-12-27 03:49:24)
·Ubuntu 上最好用的中 (2025-12-27 03:49:20)