码迷,mamicode.com
首页 > 编程语言 > 详细

C++:多线程的使用

时间:2021-05-24 04:32:11      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:single   寄存器   c++   tco   rda   程序   win   return   block   

线程的概念

线程的组成:

  栈区和栈区指针

  程序计数器:PC

  寄存器集合

线程的状态:

  新建状态(New):刚被创建

  准备状态(Runnable):加载所需的所有资源,等待CPU

  运行状态(Running):被CPU执行

  挂起状态(Blocked):阻塞,等待唤醒

 

线程和进程的区别:

1. 进程是资源分配的最小单元,线程是程序执行的最小单元。一个进程可以由一个或多个进程组成。

2. 从内存上:进程创建时会被分配地址空间,并且包含以下几种内存空间:堆区、栈区、代码区、全局变量区。

  线程创建时会分配线程的私有栈,包括:维护参数和局部变量线程栈区,程序计数器(维护线程挂起再运行),寄存器集合等。

  线程共享进程中除了线程上下文外的所有内存空间,包括(文件、系统资源等)

3. 从效率上:进程包含线程,并且拥有更多的数据结构需要维护。所以切换或者创建,进程的效率要慢于线程。

4. 安全性上:进程间有独立的地址空间,安全性较好;线程间虽然有私有的栈区,当理论上只要知道栈帧地址即可修改其他线程的变量。

 

线程的使用:

C++11之前:

1. __beginthreadex ( process.h中)

接口介绍:

unsigned long _beginthread(
  void(_cdecl *start_address)(void *), //声明为void (*start_address)(void *)形式
  unsigned stack_size, //是线程堆栈大小,一般默认为0
  void *arglist //向线程传递的参数,一般为结构体
);
 
unsigned long _beginthreadex( //推荐使用
  void *security,    //安全属性,NULL表示默认安全性
  unsigned stack_size, //是线程堆栈大小,一般默认为0
  unsigned(_stdcall  *start_address)(void *),    //声明为unsigned(*start_address)(void *)形式
  void *argilist,    //向线程传递的参数,一般为结构体
  unsigned initflag, //新线程的初始状态,0表示立即执行,CREATE_SUSPEND表示创建后挂起(可用ResumeThread唤醒)。
  unsigned *thrdaddr //该变量存放线程标识符,它是CreateThread函数中的线程ID。
); //创建成功条件下的将线程句柄转化为unsigned long型返回,创建失败条件下返回0

 

使用示例:

#include<iostream>
#include "windows.h"
#include "process.h"

using namespace std;

unsigned __stdcall add100(void*) {
    long long sum = 0;
    for (int i = 0; i < 1000000; i++) {
        sum += i;
    }
    cout << sum << endl;

    return 1;
}

int main() 
{
    // 开启线程
    unsigned int threadId;
    HANDLE hd1 = (HANDLE)_beginthreadex(NULL, 0, add100, NULL, NORMAL_PRIORITY_CLASS, &threadId);

    // 阻塞,等待线程函数结束
    WaitForSingleObject(hd1, INFINITE);

    // 获取线程函数的返回值,线程函数如果没有执行return,则返回默认值
    DWORD dwExitCode;
    GetExitCodeThread(hd1, &dwExitCode);
}

__beginthreadex内部实现是调用CreareThread,但一般不推荐直接使用CreateThread,因为前者做了许多安全保护的工作。

具体原有参考:https://www.cnblogs.com/ay-a/p/9135652.html

中介三种创建线程的方式:

1) Create/EndThread是Win32方法开始/结束一个线程
2) _beginthreadx/_endthreadex是C RunTime方式开始/结束一个线程
3) AfxBeginThread是在MFC中开始/结束一个线程 

https://www.cnblogs.com/lujin49/p/4557655.html

Note:

1. 直接在CreateThread API创建的线程中使用sprintf,malloc,strcat等涉及CRT存储堆操作的CRT库函数是很危险的,容易造成线程的意外中止。 在使用_beginthread和_beginthreadex创建的线程中可以安全的使用CRT函数。但是必须在线程结束的时候相应的调用_endthread或_endthreadex

2. _beginthread成对调用的_endthread函数内部隐式的调用CloseHandle关闭了线程句柄,而与_beginthreadex成对使用的_endthreadex则没有关闭线程的句柄,需要显示的调用CloseHandle关闭线程句柄,不要使用_beginthread,使用._beginthreadex代替之

3. 尽量不要在一个MFC程序中使用_beginthreadex()或CreateThread()。

4. 没有使用到MFC的线程尽量用_beginthreadex启动

 

C++11之后:

1. thread (thread.h中)

使用方式:所有可执行的对象都可以放入thread中,包括,全局函数、类的成员函数、lambda表达式等。

#include<iostream>
#include "thread"

using namespace std;

int add100(int cnt) {
    long long sum = 0;
    for (int i = 0; i < cnt; i++) {
        sum += i;
    }
    cout << sum << endl;

    return 1;
}

class A{
public:
    A() {}
    void test(int t) {
        this_thread::sleep_for(chrono::seconds(t));
        cout << "sleep seconds: " <<  t << endl;
    }
};

int main() 
{
    // 1. 普通函数放入线程执行
    thread t1(add100, 100000);

    // 2. lambda表达式方式线程执行
    thread t2([] {
        this_thread::sleep_for(chrono::seconds(2));
        cout << "sleep 2 seconds. " << endl;
    });

    // 3. 类的成员变量放入线程中执行
    A a;
    thread t3(&A::test, a, 3);

    t1.join();
    t2.join();
    t3.join();
}

 

join:

  等待子线程结束,阻塞。

detach:

  将主线程与子线程分离,即不需要等待子线程结束,主线程也可以退出并不会报错。

yield:

  交出当前线程的时间片,让当前线程放弃执行,让操作系统优先调用其他线程执行。

  比如某个线程要等待某个变量,如果用死循环不断判断变量会耗费CPU性能,可以在等待时调用yield,交出时间片。

while(!isDone()); // Bad
while(!isDone()) yield(); // Good

 

 

Note:

1. 线程thread对象无法被复制或拷贝,只能被move或swap

2. detach后不能调用join,同样join之后不能调用detach

 

其他关于C++11多线程的用法查考future。

C++:多线程的使用

标签:single   寄存器   c++   tco   rda   程序   win   return   block   

原文地址:https://www.cnblogs.com/dylan-liang/p/14751094.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!