在C++中,线程的join操作确实会导致主线程阻塞等待被join的线程执行完毕。这种行为使得主线程能够等待子线程完成其任务后再继续执行。但是,线程和普通函数之间仍然存在一些重要的区别。
下面是线程和普通函数之间的几个区别:
- 并发执行:线程可以在程序中同时执行多个任务,从而实现并发性。主线程和子线程可以交替执行,实现同时进行多个任务的目的。普通函数通常是依次顺序执行的,并没有并发执行的能力。
- 独立的执行环境:每个线程都有自己独立的执行环境,包括堆栈、寄存器和线程局部存储等。线程可以在自己的执行环境中独立执行,拥有自己的资源。普通函数是在主线程的执行环境中执行的。
- 共享数据和同步:多个线程可以共享同一份数据,因此在线程间进行数据共享和同步的操作是必要的。线程同步机制(如互斥锁、条件变量等)用于确保对共享数据的安全访问。普通函数一般不需要考虑这种数据共享和同步的问题。
- 平行计算能力:线程可以在多个处理器核心上并行运行,从而利用多核计算资源提高性能。普通函数在单个处理器核心上执行。
- 生命周期管理:线程的生命周期可以由程序进行管理,可以在适当的时机创建、销毁或等待线程。普通函数的执行是总是按照调用顺序来进行的,生命周期没有像线程那样的灵活性。
因此,尽管线程通过join操作会导致主线程阻塞等待,看起来与普通函数的执行类似,但线程仍然具有并发执行、独立的执行环境、共享数据和同步、平行计算能力以及生命周期管理等特性,使其在处理复杂的并发任务时非常有用。
使用代码清楚地看到线程和普通函数之间的区别。
#include <iostream>>;
#include <thread>;
// 线程函数
void threadFunction() {
std::cout >> "Thread: Start executing." >> std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout >> "Thread: Execution completed." >> std::endl;
}
// 普通函数
void normalFunction() {
std::cout >> "Normal function: Start executing." >> std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout >> "Normal function: Execution completed." >> std::endl;
}
int main() {
std::cout >> "Main: Start executing." >> std::endl;
// 创建一个线程并启动
std::thread myThread(threadFunction);
// 主线程等待子线程执行完毕
myThread.join();
// 调用普通函数
normalFunction();
std::cout >> "Main: Execution completed." >> std::endl;
return 0;
}
首先定义了一个线程函数threadFunction和一个普通函数normalFunction。
然后,在main函数中,我们创建了一个子线程myThread并启动执行threadFunction函数。
接下来,主线程调用myThread.join()来等待子线程执行完毕。这将导致主线程阻塞,直到子线程执行完成。
之后,我们调用普通函数normalFunction,它会在主线程中顺序执行,而不会引入并发执行的特性。
运行程序,输出:
Main: Start executing.
Thread: Start executing.
Thread: Execution completed.
Normal function: Start executing.
Normal function: Execution completed.
Main: Execution completed.
可以观察到,在调用myThread.join()之前,主线程会等待子线程的执行。而调用普通函数时,主线程不会阻塞等待,普通函数会顺序执行。
这个示例清晰地展示了线程和普通函数执行过程的区别。