zoukankan      html  css  js  c++  java
  • 关于线程同步与双队列性能(续)

    4遇到的问题

    1、  如何解决printf导致两个线程频繁切换?

    这个问题的解决方法在前面已经明确了,就是分配一个很大的输出缓冲区,在写到一定程度时再一次性用printf输出,避免printf的频繁调用。最好实在程序结束时输出统计和信息。

     

    2、  在使用锁之后,是否会导致线程的频繁切换?

    这也是我的疑问,后来在windows上做了一个实验证明了不同的锁会引起不同的线程调度行为。

    DWORD WINAPI ThreadProc(LPVOID)

    {

        char buf[1000];

        LARGE_INTEGER li;

        for (int i = 0; i < 100; ++i)

        {

            LOCK();  //这是个宏,用于切换使用各种锁

            test();

            QueryPerformanceCounter(&li);

            sprintf(buf, "thread id = %lu, getlock at %lu\n",

                GetCurrentThreadId(), li);

            StringOut(buf); //巨大的内存缓冲

            UNLOCK();

        }

        return 0;

    }

     

     

    DWORD WINAPI ThreadProc(LPVOID)

    {

        char buf[1000];

        LARGE_INTEGER li;

        for (int i = 0; i < 100; ++i)

        {

            LOCK();  //这是个宏,用于切换使用各种锁

            test();

            QueryPerformanceCounter(&li);

            sprintf(buf, "thread id = %lu, getlock at %lu\n",

                GetCurrentThreadId(), li);

            StringOut(buf); //巨大的内存缓冲

            UNLOCK();

        }

        return 0;

    }

     

    这个线程处理函数被两个不同的线程调用,并在输出中打印被调用的时间,在使用Critical Section时打印内容如下:

    thread id = 2200, getlock at 882789456

    thread id = 2200, getlock at 882790147

    thread id = 2200, getlock at 882790233

    <... REPEAT 96 times ...>

    thread id = 2200, getlock at 882798374

     

    thread id = 784, getlock at 882798487

    thread id = 784, getlock at 882798604

    thread id = 784, getlock at 882798693

    <... REPEAT 96 times ...>

    thread id = 784, getlock at 882805814

     

     

     

    thread id = 2200, getlock at 882789456

    thread id = 2200, getlock at 882790147

    thread id = 2200, getlock at 882790233

    <... REPEAT 96 times ...>

    thread id = 2200, getlock at 882798374

     

    thread id = 784, getlock at 882798487

    thread id = 784, getlock at 882798604

    thread id = 784, getlock at 882798693

    <... REPEAT 96 times ...>

    thread id = 784, getlock at 882805814

     

    请按任意键继续. . .

    对于Critical Section来说并不是每次上锁和解锁都会产生线程切换。

    对于Mutex 则有以下输出:

    thread id = 2360, getlock at 771657703

    <... REPEAT 15 times ...>

    thread id = 2360, getlock at 771660102

    thread id = 3316, getlock at 771660261

    thread id = 2360, getlock at 771660482

    thread id = 3316, getlock at 771660671

    thread id = 2360, getlock at 771660955

    thread id = 3316, getlock at 771661121

    <... some result ignored ...>

    thread id = 3316, getlock at 771702420

    thread id = 2360, getlock at 771702694

    thread id = 2360, getlock at 771920375

     

    请按任意键继续. . .

     

     

     

    thread id = 2360, getlock at 771657703

    <... REPEAT 15 times ...>

    thread id = 2360, getlock at 771660102

    thread id = 3316, getlock at 771660261

    thread id = 2360, getlock at 771660482

    thread id = 3316, getlock at 771660671

    thread id = 2360, getlock at 771660955

    thread id = 3316, getlock at 771661121

    <... some result ignored ...>

    thread id = 3316, getlock at 771702420

    thread id = 2360, getlock at 771702694

    thread id = 2360, getlock at 771920375

     

    请按任意键继续. . .

    这里可以看出使用Mutex锁之后,线程调度明显变得非常频繁,而且根本没有规律。这与WINDOWS的开发说明也是一样的,因为Critical Section不会在上锁和解锁时并不陷入内核,而Mutex则每次加锁和解锁都会陷入内核态,所以系统调用导致了更频繁的调度。

     

    3、  为什么在DEBUG版本中,双队列和共享队列的性能一样?甚至还比共享队列方式慢?

    不得不承认我在这里犯了一个非常愚蠢的错误,由于双队列编码的复杂性我在双队列中使用了大量的assert来保证bug能够快速被发现和修正,每个数据的处理流程中都比共享队列方案中多使用4assert,在测量1GINT数据的吞吐量时,assert语句耗时惊人。所以导致一个现象是,在DEBUG版本中双队列和共享队列方案的性能一样,而在RELEASE版本中(没有assert)双队列是共享队列方案性能的10多倍。

    非常感谢我的计算机老师何笑帮助我发现这个问题,他总是在我最困惑的情况下给于帮助,下面这个程序可以证明在一定数据量后assert的调用导致大量CPU开销。

    DWORD WINAPI ThreadProcUseAssert(LPVOID)

    {

        //N很大时,assert的开销会非常明显

        for (int i = 0; i < N; ++i)

        {

            __asm {  //阻止编译器优化掉循环

                 Nop

            }

           assert(1);

           assert(2);

           assert(3);

           assert(4);

        }

        return 0;

    }

     

     

     

    DWORD WINAPI ThreadProcUseAssert(LPVOID)

    {

        //N很大时,assert的开销会非常明显

        for (int i = 0; i < N; ++i)

        {

            __asm {  //阻止编译器优化掉循环

                 Nop

            }

           assert(1);

           assert(2);

           assert(3);

           assert(4);

        }

        return 0;

    }

     

    4、  在公司调试双队列程序时,为什么出现频繁的内存非法访问?而又找不出代码的问题?

    对于这个问题,我极端的无语!那是因为我使用了办公机中VC6下已有的一个Project文件,此文件中针对Application的配置是Single-Thread,起初一直认为是自己编写的代码有问题,定位了大概3个小时,最后发现不同线程malloc两次分配了同一个地址,这时才恍然大悟是链接了单线程版本的库函数。解决方法是在VC6菜单中的 Project->Setting将其改为Multi-Thread后想象消失。

  • 相关阅读:
    [转]三维曲线投影到坐标轴
    [转]python matplotlib contour画等高线图
    [转]python (matplotlib)画三维图像
    excel挑选出每一行的最大值或最小值
    Origin 三种方法在图片中添加水平辅助线
    Origin画放大部分区域图片
    Origin画3DScatter图-第一节
    JFreechart绘制2D散点图
    Ctex入门指南笔记 -列表、表格、公式与图片
    Ctex安装与运行
  • 原文地址:https://www.cnblogs.com/qwcbeyond/p/2042524.html
Copyright © 2011-2022 走看看