C++多线程常用的几种实现方式

1、std::thread
C++11标准之后便引入了线程库std::thread。无论是windows或是Linux开发者都可以非常简单的通过这种方式,在C++程序中创建和管理线程。

示例代码:

#include <unistd.h>
#include <iostream>
#include <thread>

void thread_fun(void)
{
    std::cout << "thread_fun id: " << std::this_thread::get_id() << std::endl;
    sleep(1);
    std::cout << "thread_fun exit." << std::endl;
}

int main(int argc, char *argv[])
{
    std::cout << "main id: " << std::this_thread::get_id() << std::endl;

    std::thread testthread(thread_fun);
    testthread.join();

    std::cout << "main exit." << std::endl;

    return 0;
}

运行结果:
在这里插入图片描述
可以看到thread_fun处于另一个线程中执行。

且该子线程是一个阻塞的操作,主线程等待子线程返回后才继续往下执行,是因为我们对线程进行了join()操作,join()表示主线程要等待子线程返回后对其资源进行回收,所以它必须要等待子线程执行完成,才去执行join()后面的逻辑。因此,在实际的使用中调用join()的位置也是比较关键的。

当主线程不需要等待子线程的结果时,可以使用detach()分离操作。detach()会使子线程与主线程分离,不会阻塞主线程的运行,分离后便与主线程无关了,资源由系统回收。

示例代码:

#include <unistd.h>
#include <iostream>
#include <thread>

void thread_fun(void)
{
    std::cout << "thread_fun id: " << std::this_thread::get_id() << std::endl;
    sleep(1);
    std::cout << "thread_fun exit." << std::endl;
}

int main(int argc, char *argv[])
{
    std::cout << "main id: " << std::this_thread::get_id() << std::endl;

    std::thread testthread(thread_fun);
    testthread.detach();

    std::cout << "main exit." << std::endl;

    while (1)
    {
        pause();
    }

    return 0;
}

运行结果:
在这里插入图片描述
可以看到子线程没有阻塞主线程,detach()之后继续往下运行了。与join不同的是,在detach后面我们对主线程增加了while (1)的循环操作,防止主线程退出。如果不加循环等待会怎么样呢?

当把以上示例代码while (1)部分的代码删除,重新编译运行,会发现thread_fun子线程部分还没来得及执行,进程就直接退出了。
在这里插入图片描述
这是因为,detach()操作其实只是把子线程分离单独执行,而主线程的退出往往会伴随着整个进程退出,当进程退出时,所有的子线程也会随之终止。总的来说,即便子线程标记为detach()分离状态,也是与主线程“同生死,共存亡”的。

同时,C++标准库中也提供有一系列类库,用于实现多线程之间的数据同步及通信。如互斥锁std::mutex、条件变量std::condition_variable、互斥锁自动管理器std::lock_guard等。

2、pthread
对于Linux系统,pthread是Linux下的最常用的线程库,与std::thread不同,pthread是基于C语言实现。

有几个常用的数据类型及函数需要先了解一下:

pthread_t:标识线程ID。它是一个结构体数据类型,用于唯一标识一个线程。

pthread_attr_t:线程属性。可用于设置线程的堆栈地址、堆栈大小、优先级等。

pthread_mutex_t:互斥锁。用于线程同步,保护共享资源免受多个线程的同时访问。

pthread_create():创建一个线程。

pthread_self():获取当前线程ID。

pthread_join():阻塞当前的线程,直到另外一个线程运行结束。

pthread_detach():分离线程。类似于std::thread的detach()。

pthread_exit():终止当前线程。

pthread_attr_init():初始化线程属性。

pthread_attr_destroy():删除线程的属性。

pthread_attr_setschedparam():通过设置线程属性,设置线程优先级。

pthread_attr_setstacksize():通过设置线程属性,设置线程堆栈大小。

pthread_mutex_init():初始化互斥锁。

pthread_mutex_lock():互斥锁上锁。

pthread_mutex_unlock():互斥锁解锁。

pthread_mutex_destroy():销毁互斥锁。

以下通过一段示例代码,了解下pthread的常用用法:

#include <iostream>
#include <unistd.h>
#include <pthread.h>

static void *pthread_func(void *param)
{
  pthread_detach(pthread_self());                   //分离线程
  printf("pthread_func id=%ld\n", pthread_self());  //打印线程ID

  int count = 0;
  while(1)
  {
    sleep(2);
    printf("count=%d\n", count++);
  }
  return NULL;
}

int main(int argc, char *argv[])
{
  printf("main id=%ld\n", pthread_self());

  pthread_t p_tid;
  pthread_attr_t p_attr;
  sched_param param;
  param.sched_priority = 10;
  pthread_attr_init(&p_attr);                        //初始化线程属性
  pthread_attr_setschedparam(&p_attr, &param);       //设置线程优先级为10
  pthread_attr_setstacksize(&p_attr, 1024*1024);     //设置堆栈大小为1M
  pthread_create(&p_tid, &p_attr, pthread_func, NULL);  //创建线程

  while(1)
  {
    pause();
  }

  return 0;
}

在这里插入图片描述
通过以上代码便创建了一个线程,并将该线程与主线程分离,独立运行。需要注意的是,当需要把类成员函数作为线程函数的时候,需要把类成员函数声明为static静态函数才可。

当线程中涉及到数据共享时,就需要考虑到数据资源同步问题,这时就应该对同步的数据加锁处理。示例如下:

#include <iostream>
#include <unistd.h>
#include <pthread.h>

pthread_mutex_t m_mutex = PTHREAD_MUTEX_INITIALIZER;  //静态初始化
volatile int m_count = 0;

static void *pthread_func(void *param)
{
  pthread_detach(pthread_self());            //分离线程

  while(1)
  {
    sleep(1);
    pthread_mutex_lock(&m_mutex);
    printf("pthread_func id=%ld  m_count=%d\n", pthread_self(), m_count++);
    pthread_mutex_unlock(&m_mutex);
  }

  return NULL;
}

int main(int argc, char *argv[])
{
  printf("main id=%ld\n", pthread_self());

  //pthread_mutex_init(&m_mutex, NULL);          //动态初始化

  pthread_t p_tid;
  pthread_attr_t p_attr;
  sched_param param;
  param.sched_priority = 10;
  pthread_attr_init(&p_attr);              //初始化线程属性
  pthread_attr_setschedparam(&p_attr, &param);    //设置线程优先级为10
  pthread_attr_setstacksize(&p_attr, 1024*1024);     //设置堆栈大小为1M
  pthread_create(&p_tid, &p_attr, pthread_func, NULL);  //创建线程1
  pthread_create(&p_tid, &p_attr, pthread_func, NULL);  //创建线程2

  while(1)
  {
    pause();
  }

  return 0;
}

在这里插入图片描述
对于pthread_mutex_t互斥锁的初始化有两种方式,静态初始化和动态初始化。动态初始化即使用上述提及的pthread_mutex_init()函数,静态初始化则在编译时初始化锁为解锁状态。

3、CreateThread

CreateThread 函数是 Windows API 中用于创建新线程的一个非常重要的函数。在 Win32 编程中,线程是系统分配处理器时间资源的基本单元,它是进程中的实际运作单位。通过使用 CreateThread可以在同一进程中创建多个线程。
CreateThread 函数原型如下:

HANDLE CreateThread(  
  LPSECURITY_ATTRIBUTES   lpThreadAttributes,  
  SIZE_T                  dwStackSize,  
  LPTHREAD_START_ROUTINE  lpStartAddress,  
  LPVOID                  lpParameter,  
  DWORD                   dwCreationFlags,  
  LPDWORD                 lpThreadId  
);

参数说明:

lpThreadAttributes:指向SECURITY_ATTRIBUTES结构的指针,用于指定线程的安全属性。如果为 NULL,则线程将使用默认安全属性。

dwStackSize:指定线程堆栈的初始大小(以字节为单位)。如果设置为 0,则堆栈大小将与创建它的线程的堆栈大小相同。

lpStartAddress:指向线程函数的指针,表示新线程的入口点。

lpParameter:传递给线程函数的参数。

dwCreationFlags:控制线程创建的标志。它的值可以是以下两种之一: 0(表示线程创建后立即运行)或CREATE_SUSPENDED(表示线程创建后暂停运行,直到调用ResumeThread函数)。

lpThreadId:指向DWORD变量的指针,用于接收新线程的标识符。如果为NULL,则不返回线程标识符。

返回值:
如果函数执行成功,CreateThread 返回一个指向新线程的句柄。如果函数调用失败,则返回值为 NULL。

示例代码:

#include <windows.h>  
#include <stdio.h>  

DWORD WINAPI ThreadFunc(LPVOID lpParam)
{
    printf("Hello!!!\n");
    return 0;
}

int main(int argc, char *argv[])
{  
    HANDLE hThread;  
    DWORD threadId;  
  
    //创建线程 
    hThread = CreateThread(NULL, 0, ThreadFunc, NULL, 0, &threadId);  
  
    if (hThread == NULL)
    {  
        printf("创建线程失败 (%d)\n", GetLastError());  
        return -1;
    }
    //等待线程结束
    WaitForSingleObject(hThread, INFINITE);
  
    //关闭线程句柄
    CloseHandle(hThread);

    return 0;  
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ghx3110

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值