C语言多线程的引入

来源:互联网 发布:mac office 卸载 编辑:程序博客网 时间:2024/06/05 06:38

前言

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

正文

1.CreateThread

先来看一个实例

// cpp_mutex.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#include <process.h>#include <iostream>#include <stdlib.h>#include <stdio.h>#include <Windows.h>#pragma warning(disable:4996)using namespace std;DWORD WINAPI run(void *p){char *mess = (char *)p;printf("线程%d,弹窗\n", GetCurrentThreadId());char threadId[20];sprintf(threadId,"线程%d",GetCurrentThreadId());MessageBoxA(0,mess,threadId,0);return 0;}int main(){cout << "C语言多线程演示" << endl;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

[cpp] view plain copy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <process.h>  
  4. #include <Windows.h>  
  5. void hello(void *p)  
  6. {  
  7.     printf("线程%d, say Hello World\n", GetCurrentThreadId());  
  8. }  
  9. int main()  
  10. {  
  11.     printf("******C语言多线程演示***by David***\n");  
  12.     HANDLE handles[5];  
  13.     for (int i = 0; i < 5; i++)  
  14.     {  
  15.         handles[i] = _beginthread(hello, 0, NULL);  
  16.     }  
  17.     WaitForMultipleObjects(5, handles, 1, INFINITE);  
  18.     getchar();  
  19.     return 0;  
  20. }  
运行


_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的使用说明:

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


总结

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


多线程编程面临的问题

  1. 多个子线程对同一个全局变量进行操作,容易引起线程冲突,也就是子线程的互斥问题。
  2. 主线程与子线程之间的同步问题。
后续文章,会对这两个问题的解决进行探讨。
原文地址:http://blog.csdn.net/zhangxiangdavaid/article/details/43700485

0 0
原创粉丝点击