场景

1.写普通的程序时, 经常会使用cout来做输出, 每个进程只有一个控制台, 如果多线程调用cout时会出状况吗?

2.之所以研究cout会不会在并发下调用有问题, 是因为曾经有一个bug的崩溃点正好在cout处.

3.参考vc++的说明, iostream库的对象在并发write时是不会有问题的,原因是加了临界区,看osfinifo.c. 以下是对vc++针对stl库的线程安全的说明.

注: 虽然标准库说了支持cout并发, 但是在vs2010里出现过 ioinfo *pio = _pioinfo(fh); 这个 pio 为NULL的情况, 导致崩溃. 所以标准库实现也有BUG, 建议还是别用cout做并发输出.

osfinfo.c


int __cdecl __lock_fhandle (
        int fh
        )
{
        ioinfo *pio = _pioinfo(fh);
        int retval=TRUE;

        /*
         * Make sure the lock has been initialized.
         */
        if ( pio->lockinitflag == 0 ) {

            _mlock( _LOCKTAB_LOCK );
            __TRY
                if ( pio->lockinitflag == 0 ) {
                    if ( !InitializeCriticalSectionAndSpinCount( &(pio->lock), _CRT_SPINCOUNT )) {
                        /*
                         * Failed to initialize the lock, so return failure code.
                         */
                        retval=FALSE;
                    }
                    pio->lockinitflag++;
                }
            __FINALLY
                _munlock( _LOCKTAB_LOCK);
            __END_TRY_FINALLY
        }

        if(retval)
        {
            EnterCriticalSection( &(_pioinfo(fh)->lock) );
        }

        return retval;
}

void __cdecl _unlock_fhandle (
        int fh
        )
{
        LeaveCriticalSection( &(_pioinfo(fh)->lock) );
}

说明

1.以下线程安全的规则适用于标准C++库(除了shared_ptr 和 iostream类):

2.多线程读取一个单一的对象是线程安全. 比如给定一个对象A, 线程1 和线程2同时读取这个A对象是线程安全的.

3.如果一个对象正在被一个线程写, 那么所有对这个对象的读和写在其他线程里必须被保护起来(加锁). 比如, 给定一个对象A, 如果线程1 正在写入A, 那么必须阻止线程2读取或写入A.

4.一个线程对一个类型的实例的读写时, 另一个线程正对这个类型的另一个实例进行读写是安全的. 例如: 给定 A1和A2对象实例,它们属于同样类型A, 线程1中对A1进行写,而线程2中对A2进行读, 这种方式是安全的.

shared_ptr

1.多线程能同时对不同的shared_ptr对象进行读写, 即使这些对象是拥有共享所有权的复制品.

iostream 类

1.iostream 类和其他类遵守相同的规则, 除了一个例外, 它允许多线程进行写入对象. 比如, 线程1和线程2允许同时对cout进行写入. 这样也仅会导致输出混淆.

例子

  1. 以下例子验证了cout可允许多线程同时写, 并且std::vector可以同时读.
  2. 既然知道了标准库的对象特性, 这个问题自然知道原因. std::vector的多线程读写问题

// test-stl-thread-safety.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <Windows.h>
#include <iostream>
#include <vector>

static const int THREADCOUNT = 10;

DWORD WINAPI ThreadFunc(LPVOID param)
{
    auto params = (std::vector<int>*)param;
    for(int i = 0; i< 2000; ++i)
    {
        auto ite = params->begin();
        for(; ite!= params->end();++ite)
            std::cout << "Thread: " << GetCurrentThreadId() << " i: " << i << " params: " << *ite << std::endl;
    }
    return 0;
}

void TestConcurrentCout()
{
    HANDLE hThread[THREADCOUNT];
    std::vector<int> params;
    params.push_back(0);
    params.push_back(1);
    params.push_back(2);
    params.push_back(3);
    params.push_back(4);

    for(int i = 0; i< THREADCOUNT;++i)
    {
        hThread[i] = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE) ThreadFunc,
         (LPVOID)&params,
         0,
         NULL);
    }

    for (int i = 0; i < THREADCOUNT; i++)
      WaitForSingleObject(hThread[i], INFINITE);
}

int _tmain(int argc, _TCHAR* argv[])
{
    TestConcurrentCout();
    return 0;
}

参考

Thread Safety in the Standard C++ Library

最新文章

  1. iOS-公司开发者账号的申请和注册(博主原创+亲身经历+2016年申请+附带与邓白氏公司的往来邮件截图)
  2. ubuntu14.04安装django
  3. c#委托(1)
  4. 用.htaccess获取文件夹和文件名
  5. SpringMVC框架入门配置 IDEA下搭建Maven项目
  6. scope重定义
  7. android 官方教程中文版
  8. Android App性能优化笔记之一:性能优化是什么及为什么?
  9. delphi 发送Windwos消息控制按钮(控制计算器里的某一个按钮)
  10. 开启和禁用Wifi热点命令
  11. js 常用插件
  12. HDU1548:A strange lift(Dijkstra或BFS)
  13. Python学习笔记(一):列表和元组
  14. [M$]重装或更换主板后提示“由于指定产品密钥激活次数“ office 2016
  15. XShell发送命令到全部会话
  16. component lists rendered with v-for should have explicit keys
  17. HTML Agility Pack:簡單好用的快速 HTML Parser
  18. oracle中extract()函数----用于截取年、月、日、时、分、秒
  19. Spark特征(提取,转换,选择)extracting, transforming and selecting features
  20. myFocus 焦点图/轮播插件

热门文章

  1. 发布MVCIIS报错未能加载文件或程序
  2. Linux ip命令详解
  3. 多台服务器共享session问题
  4. C++ 读书笔记1
  5. Hadoop HBase概念学习系列之HBase表的一些设置(强烈推荐好好领悟)(十三)
  6. UserUI程序实现过程简述
  7. Call to undefined function mysqli_connect() in xx.连接数据库出现mysqli_connect()未定义的问题。
  8. 在C#应用程序中,利用表值参数过滤重复,批量向数据库导入数据,并且返回重复数据
  9. DOM、JDOM、DOM4J的区别
  10. iOS: 聊聊 Designated Initializer(指定初始化函数):NS_DESIGNATED_INITIALIZER