C++多线程编程

线程

概念

线程在Unix系统下,通常被称为轻量级的进程,线程虽然不是进程,但却可以看作是Unix进程的表亲,同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。但同一进程中的多个线程有各自的调用栈(call stack),自己的寄存器环境(register context),自己的线程本地存储(thread-local storage)。 一个进程可以有很多线程,每条线程并行执行不同的任务。

优点

线程可以提高应用程序在多核环境下处理诸如文件I/O或者socket I/O等会产生堵塞的情况的表现性能。在Unix系统中,一个进程包含很多东西,包括可执行程序以及一大堆的诸如文件描述符地址空间等资源。在很多情况下,完成相关任务的不同代码间需要交换数据。如果采用多进程的方式,那么通信就需要在用户空间和内核空间进行频繁的切换,开销很大。但是如果使用多线程的方式,因为可以使用共享的全局变量,所以线程间的通信(数据交换)变得非常高效。

C++

一个简单的Demo

#include <iostream>
#include <thread>
#include <future>
using namespace std; 
void helloworld()
{
    cout << "hello world \n"; 
}
int main()
{   
    //开启一个线程 
    std::thread t(helloworld);
    std::cout << "hello world main thread\n";

    //线程的终结
    t.join(); 

    return 0;
}

多线程库

C++11中终于提供了多线程的标准库,提供了线程管理、保护共享数据、线程间同步操作、原子操作等类。

多线程库对应的头文件是#include<thread> ,类名为std::thread

一个简单的串行程序如下:

#include <iostream>
#include <thread>

void function_1() {
    std::cout << "I'm function_1()" << std::endl;
}

int main() {
    function_1();
    return 0;
}

这是一个典型的单线程的单进程程序,任何程序都是一个进程,main()函数就是其中的主线程,单个线程都是顺序执行。

将上面的程序改造成多线程程序其实很简单,让function_1()函数在另外的线程中执行:

#include <iostream>
#include <thread>

void function_1() {
    std::cout << "I'm function_1()" << std::endl;
}

int main() {
    std::thread t1(function_1);
    // do other things
    t1.join();
    return 0;
}

分析:

  1. 首先,构建一个std::thread对象t1,构造的时候传递了一个参数,这个参数是一个函数,这个函数就是这个线程的入口函数,函数执行完了,整个线程也就执行完了。
  2. 线程创建成功后,就会立即启动,并没有一个类似start的函数来显式的启动线程。
  3. 一旦线程开始运行, 就需要显式的决定是要等待它完成(join),或者分离它让它自行运行(detach)。注意:只需要在std::thread对象被销毁之前做出这个决定。这个例子中,对象t1是栈上变量,在main函数执行结束后就会被销毁,所以需要在main函数结束之前做决定。
  4. 这个例子中选择了使用t1.join(),主线程会一直阻塞着,直到子线程完成,join()函数的另一个任务是回收该线程中使用的资源。

线程对象和对象内部管理的线程的生命周期并不一样,如果线程执行的快,可能内部的线程已经结束了,但是线程对象还活着,也有可能线程对象已经被析构了,内部的线程还在运行。

线程创建

下面的程序,我们可以用它来创建一个 POSIX 线程:

#include <pthread.h>
pthread_create (thread, attr, start_routine, arg) 

在这里,pthread_create 创建一个新的线程,并让它可执行。下面是关于参数的说明:

参数 描述
thread 指向线程标识符指针。
attr 一个不透明的属性对象,可以被用来设置线程属性。您可以指定线程属性对象,也可以使用默认值 NULL。
start_routine 线程运行函数起始地址,一旦线程被创建就会执行。
arg 运行函数的参数。它必须通过把引用作为指针强制转换为 void 类型进行传递。如果没有传递参数,则使用 NULL。

创建线程成功时,函数返回 0,若返回值不为 0 则说明创建线程失败。

终止线程

使用下面的程序,我们可以用它来终止一个 POSIX 线程:

#include <pthread.h>
pthread_exit (status) 

在这里,pthread_exit 用于显式地退出一个线程。通常情况下,pthread_exit() 函数是在线程完成工作后无需继续存在时被调用。

如果 main() 是在它所创建的线程之前结束,并通过 pthread_exit() 退出,那么其他线程将继续执行。否则,它们将在 main() 结束时自动被终止。

一些函数

  1. std::this_thread::yield: 当前线程放弃执行,操作系统调度另一线程继续执行。即当前线程将未使用完的“CPU时间片”让给其他线程使用,等其他线程使用完后再与其他线程一起竞争"CPU"。
  2. std::this_thread::sleep_for: 表示当前线程休眠一段时间,休眠期间不与其他线程竞争CPU,根据线程需求,等待若干时间。

原文链接: https://www.cnblogs.com/guoben/p/12895783.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    C++多线程编程

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/348560

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年3月2日 上午5:08
下一篇 2023年3月2日 上午5:08

相关推荐