您的位置:

C++多线程编程:std::thread的用法

一、std::thread概述

std::thread是C++11提供的多线程库之一,它提供了一种方便的方式来并行执行代码,将多个任务同时执行。

std::thread库提供了创建线程、等待线程结束和管理线程的方法。在std::thread库中,我们可以使用std::thread对象的构造函数来创建一个新的线程,并告诉它应该执行哪个函数或lambda表达式。当执行完毕时,线程对象可以用join()或detach()方法来终止线程并等待它的结束。

void func1()
{
    // ...
}

int main()
{
    std::thread t(func1);
    // ...
    t.join();
    return 0;
}

在上面的代码中,我们定义了一个名为func1()的函数,并使用std::thread对象t在新线程中执行func1()函数。使用join()方法等待线程结束。在主线程中,我们可以执行其他操作,并在最后等待线程结束。

二、std::thread的创建和声明

std::thread对象的创建方式有多种方法,这里列举其中的两种方法。

第一种,使用函数指针创建线程:

// 一个简单的函数
void task1(int n)
{
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    std::cout << "task1: " << n << std::endl;
}

int main()
{
   std::thread t(task1, 1); // task1函数的参数是1
   std::cout << "main thread\n";
   t.join();
   return 0;
}

第二种,使用lambda表达式创建线程:

int main()
{
    std::thread t([](int n) {
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
        std::cout << "task2: " << n << std::endl;
    }, 2);
    std::cout << "main thread\n";
    t.join();
    return 0;
}

注意:使用lambda表达式时,需要在参数列表中指定要传递给线程的值。

三、std::thread的数据共享和同步

多线程编程时,可能会遇到数据共享和同步的问题。对于数据共享的问题,很容易就会想到使用全局数据或静态数据。

// 全局变量
int g_count = 0;

void task1()
{
    for (int i = 0; i < 5; ++i) {
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
        ++g_count;
    }
}

int main()
{
    std::thread t1(task1);
    std::thread t2(task1);

    t1.join();
    t2.join();

    std::cout << "g_count = " << g_count << std::endl;
    return 0;
}

然而,在多个线程中同时访问全局变量或静态变量时,可能会出现数据竞争。在上面的示例中,我们想要将g_count变量递增五次,但在实际执行过程中,可能出现每个线程各自的计数和。

因此,我们需要使用互斥量来同步访问。

std::mutex g_mutex;
int g_count = 0;

void task2()
{
    for (int i = 0; i < 5; ++i) {
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
        std::lock_guard<std::mutex> lock(g_mutex);
        ++g_count;
    }
}

int main()
{
    std::thread t3(task2);
    std::thread t4(task2);

    t3.join();
    t4.join();

    std::cout << "g_count = " << g_count << std::endl;
    return 0;
}

在上面的示例中,我们使用了互斥量g_mutex,通过std::lock_guard<std::mutex> lock(g_mutex)来保护对g_count变量的访问。

四、std::thread的一些其他方法

std::thread还提供了一些其他方法来管理线程。

在主线程中,我们可以通过调用std::this_thread::get_id()来获取当前线程的id。

std::cout << "main thread id = " << std::this_thread::get_id() << std::endl;

在线程中,我们可以使用std::thread::yield()来暂停当前线程,允许其他线程执行。

void task3()
{
    for (int i = 0; i < 5; ++i) {
        std::this_thread::yield();
        std::cout << "task3\n";
    }
}

int main()
{
    std::thread t5(task3);

    for (int i = 0; i < 5; ++i) {
        std::cout << "main\n";
    }

    t5.join();
    return 0;
}

在上面的示例中,我们通过std::thread::yield()方法来将控制权交给其他线程,以便其他线程有机会执行。

std::thread还提供了detach()方法来在后台运行线程。

void task4()
{
    for (int i = 0; i < 5; ++i) {
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
        std::cout << "task4: " << i << std::endl;
    }
}

int main()
{
    std::thread t6(task4);
    t6.detach();
    std::cout << "main thread\n";
    return 0;
}

在上面的示例中,我们使用detach()方法来后台运行线程t6,并且在主线程中继续执行其他操作。

五、std::thread的注意事项

在使用std::thread时,需要注意以下几点:

  • 使用std::this_thread::sleep_for()或std::this_thread::sleep_until()代替std::sleep()。
  • 不能将std::thread对象的拷贝赋值给其他变量或传递给函数。
  • std::thread应该在函数结束前被join()或detach()。
  • 对于临界区的访问,应该使用std::lock_guard<std::mutex>或std::unique_lock<std::mutex>。

六、总结

本文介绍了std::thread库的一些基本用法,包括声明和创建线程、线程的数据共享和同步、std::thread的一些其他方法以及注意事项。在多线程编程时,需要特别注意线程安全和数据同步,避免出现数据竞争等问题。