赞
踩
最近学习多线程的时候,创建了一堆线程,然后每个线程都运行这个方法:
void *start_routine(void *args)
{
ThreadData *td = static_cast<ThreadData *>(args); // 安全的进行强制类型转化
int cnt = 10;
while (cnt)
{
cout << "cnt :" << cnt <<" "<< "&cnt:" << &cnt << endl;
cnt--;
sleep(1);
}
// delete td;
return nullptr;
}
出现了下面这种情况,使用cout函数打印输出在显示器上面的内容很混乱。
我自己认为是cout
函数输出内容到stdout文件
时,在这个线程还未输出完整的情况下又被别的线程调度然后又继续cout
导致了最后刷新缓冲区就成了这样子。
上述出现错误的完整代码:
#include <iostream> #include <cstdlib> #include <string> #include <cassert> #include <vector> #include <pthread.h> #include <unistd.h> using namespace std; class ThreadData { public: int number; pthread_t tid; char namebuffer[64]; }; void *start_routine(void *args) { ThreadData *td = static_cast<ThreadData *>(args); // 安全的进行强制类型转化 int cnt = 10; while (cnt) { cout << "cnt :" << cnt <<" "<< "&cnt:" << &cnt << endl; cnt--; sleep(1); } return nullptr; } int main() { //创建一批线程 vector<ThreadData *> threads; #define NUM 10 for (int i = 0; i < NUM; i++) { ThreadData *td = new ThreadData(); td->number = i + 1; snprintf(td->namebuffer, sizeof(td->namebuffer), "%s:%d", "thread", i + 1); pthread_create(&td->tid, nullptr, start_routine, td); threads.push_back(td); } for (auto &iter : threads) { cout << "create thread: " << iter->namebuffer << " : " << iter->tid << " suceesss" << endl; } while(true) { sleep(2); cout<<"mainthred"<<endl; } return 0; }
错误原因:如果多个线程同时调用cout
输出数据,由于cout
不是线程安全的,不是原子操作(下面会说什么是原子操作),所以可能会出现数据交错或混乱的情况。这是因为多个线程可能会同时访问和修改cout的输出缓冲区,导致数据交错或丢失。
std::cout的输出操作通常涉及到多个步骤,例如格式化数据、写入缓冲区、输出到终端等,这些步骤可能会在不同的线程中交错执行,从而导致输出混乱。
cout函数的缓冲区策略:在默认情况下,cout函数使用行缓冲区(line-buffered)策略,即每当输出一个换行符时,缓冲区就会被刷新。此外,如果缓冲区已满,也会自动刷新缓冲区。但是,如果程序异常退出或者使用endl或flush等函数强制刷新缓冲区,也会导致缓冲区被刷新。
多线程环境下,cout输出语句可能出现的问题,对于下面的输出语句:
std::cout << "hello" << cnt << std::endl;
多个线程同时执行这个输出语句,那么可能会出现以下情况:
printf("cnt : %d &cnt:%p\n",cnt,&cnt);
printf
函数通常不是原子操作。这意味着两个或多个线程可能会同时尝试打印到控制台,并且输出可能会交错或损坏。但是,printf 函数在用于将输出打印到单个流时是原子操作。这是因为 printf 函数是作为单个系统调用实现的,这意味着在任何其他线程可以访问流之前,它保证由一个线程完整执行。
printf内部会使用同步机制来确保输出的数据不会被其他线程中断。具体来说,printf通常会使用文件锁或互斥锁来保证同一时间只有一个线程在输出数据,从而避免输出混乱或数据交错的问题。
例如:
文件锁定是一种允许进程独占访问文件的机制。这意味着一次只有一个进程可以访问该文件。当进程锁定文件时,将阻止其他进程访问该文件,直到释放锁定为止。
互斥锁是一种同步原语,它允许线程锁定资源,以便一次只有一个线程可以访问它。当线程锁定互斥锁时,将阻止其他线程访问资源,直到释放锁。
使用方法:
#include <iostream> #include <thread> #include <mutex> using namespace std; mutex mtx; void print_message(string message) { // Lock the mutex mtx.lock(); // Print the message cout << message << endl; // Unlock the mutex mtx.unlock(); } int main() { // Create two threads thread t1(print_message, "Hello, world!"); thread t2(print_message, "Goodbye, world!"); // Wait for the threads to finish t1.join(); t2.join(); return 0; }
代码工作原理的说明:
mutex
print_message()
main()
函数创建两个线程。每个线程使用不同的消息调用函数。print_message()
join()
确保一次只有一个线程可以访问该对象。这可以防止输出交错或损坏。
不会被线程调度机制打断的操作
原子操作是指在计算机中执行的一种操作,它要么完全成功完成,要么完全不执行,没有中间状态。原子操作通常是一个单个的、不可分割的操作,可以被看作是一种基本操作。原子操作的目的是确保多个并发的执行线程或进程能够正确地协作,而不会产生竞争条件或死锁问题。
原子操作通常用于对共享资源进行访问和修改的情况,例如在多线程编程中,多个线程可能同时访问和修改同一个变量,这时就需要使用原子操作来确保操作的正确性。常见的原子操作包括读取和写入一个共享变量、递增或递减一个计数器、测试和设置一个标志等。
如有错误或者不清楚的地方欢迎私信或者评论指出
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/721075
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。