假设主线程为main,主线程开启的线程为A。1、main和A会访问同一种资源,A线程中写,B线程中读,比如全局变量int g_Some。
main和A之间是否需要同步?
2、程序中定义了某个全局函数void globalFuc()。
void globalFuc()
{
......
读取全局变量的代码
}
main线程中某处会调用到globalFuc函数,我的问题是——
是否需要
EnterCriticalSection(&cs);
globalFunc();
LeaveCriticalSection(&cs);
也就是说,main线程的时间片会不会在程序正执行到全局函数globalFunc中的某处代码时到期,从而使线程A执行?
3、main线程中若干处会读取g_Some的值,像下边这样:
int someValue1=g_Some;
......
......
用g_Some的值作某些计算或处理(此处有若干行代码都用到g_Some)
......
问题是:怎样同步?是这样?
第一种——
EnterCriticalSection(&cs);int someValue1=g_Some;
......
......
......
用g_Some的值作某些计算或处理(此处有若干行代码都用到g_Some)
......LeaveCriticalSection(&cs);还是?第二种——
EnterCriticalSection(&cs);int someValue1=g_Some;//引出问题——这里是否需要同步??只有一行代码LeaveCriticalSection(&cs);......
......
......EnterCriticalSection(&cs);用g_Some的值作某些计算或处理(此处有若干行代码都用到g_Some)
......LeaveCriticalSection(&cs);两种方式哪种较好?
由于最近项目中经常遇到这样的问题,自己没弄明白,很着急,恳请高手解答,万分感谢。

解决方案 »

  1.   

    同步放到操作公共资源的地方:
    read()
    {
    LeaveCriticalSection(&cs);
    int temp = g_global; // 读
    EnterCriticalSection(&cs);
    }write()
    {
    LeaveCriticalSection(&cs);
    g_global = 10; // 写
    EnterCriticalSection(&cs);
    }
      

  2.   

    感谢dahaiI0的解答,今天我没同步尝试运行我的程序一段时间,没发现出问题。
    但为保险起见,还是不敢不加同步,看来线程同步确实比较麻烦。
    我的程序大概有30000行左右代码,涉及到的全局变量很多,感觉主线程中需要加同步锁的地方太多了,搞的头有点大。
    开的线程,主要是用于CAN总线数据的接收,接收到的数据保存到全局变量里。
    主线程有需要的地方从全局变量中读数据,处理,有没有好一些的解决办法?
      

  3.   


    如果你所说的同一种资源单单是一个内部类型,比如Int 而你所谓的更新线程又足以及时调用的话 我个人觉得是没必要进行同步的,当然你如果非要加也没什么不可以,如果是自定义类型,比如结构体  那你最好还是加上同步;至于第二个问题 不好意思 没细看 所答非所问了 但是我觉得如果你只是为了保证界面更新数据的一致性的话,你是不是可以考虑一下这样写:EnterCriticalSection(&cs);  int someValue1=g_Some; LeaveCriticalSection(&cs); 以后所有的处理你都用someValue1 而不是再去访问g_Some,如果此时g_Some有更新的话会在下一次主线程函数得以执行的时候将其更新上去,这样不知道能不能满足你的要求。还有各位大神啊 1楼的所谓的同步起到效果了么? 打比方说 A  B两个线程 A进入临界区但没出来,这时候A的时间片到期,B线程执行,上来直接先退出临界区了,根本没起到作用啊。 
      

  4.   

    必需要同步。Windows是个抢占式多线程环境。一个线程可以随时中断运行,而另一个线程则可以随时继续执行。一个线程的运行周期20ms,Windows可能在任意时刻调用任意线程。
    给你举个例子:long g_X = 0;
    DOWRD WINAPI ThreadFunc1(PVOID pvParam){
        g_x++;
        return(0);
    }
    DOWRD WINAPI ThreadFunc2(PVOID pvParam){
        g_x++;
        return(0);
    }在这个代码中,声明了一个全局变量g_x,并将它初始化为0。现在,假设创建两个线程,
    一个线程执行ThreadFunc1,另一个线程执行ThreadFunc2。这两个函数中的代码是相同的,它
    们都将1添加给全局变量g_x。因此,当两个线程都停止运行时,你可能希望在g_x中看到2这个
    值。但是你真的看到了吗?回答是,也许看到了。根据代码的编写方法,你无法说明g_x中最
    终包含了什么东西。
    当两个线程都将g_x的值递增之后, g_x中的值就变成了2。这很好,并且正是我们希望的:
    即取出零( 0),两次将它递增1,得出的值为2。太好了。不过不要急,Windows是个抢占式多
    线程环境。一个线程可以随时中断运行,而另一个线程则可以随时继续执行。这样,上面的代
    码就无法完全按编写的那样来运行。如果代码按这种形式来运行, g_x中的最后值就不是2,而是你预期的1。这使人感到非常
    担心,因为你对调度程序的控制能力非常小。实际上,如果有100个线程在执行相同的线程函
    数,当它们全部退出之后, g_x中的值可能仍然是1。显然,软件开发人员无法在这种环境中工
    作。我们希望在所有情况下两次递增0产生的结果都是2。另外,不要忘记,编译器生成代码的
    方法,哪个CPU在执行这个代码,以及主计算机中安装了多少个CPU等因素,决定了产生的结
    果可能是不同的。这就是该环境的运行情况,我们对此无能为力。但是, Windows确实提供了
    一些函数,如果正确地使用这些函数,就能确保产生应用程序的代码得到的结果。
    为了解决上面的问题,需要某种比较简单的方法。我们需要一种手段来保证值的递增能够
    以原子操作方式来进行,也就是不中断地进行。互锁的函数家族提供了我们需要的解决方案。
    互锁的函数尽管用处很大,而且很容易理解,却有些让人望而生畏,大多数软件开发人员用得
    很少。所有的函数都能以原子操作方式对一个值进行操作。让我们看一看下面这个InterlockedExchangeAdd函数:LONG InterlockedExchangeAdd(
        PLONG plAddend,
        LONG lIncrement);
    这是个最简单的函数了。只需调用这个函数,传递一个长变量地址,并指明将这个值递增
    多少即可。但是这个函数能够保证值的递增以原子操作方式来完成。因此可以将上面的代码重
    新编写为下面的形式:long g_x = 0;
    DOWRD WINAPI ThreadFunc1(PVOID pvParam){
        InterlockedExchangeAdd(&g_x,1);
        return (0);
    }
    DOWRD WINAPI ThreadFunc2(PVOID pvParam){
        InterlockedExchangeAdd(&g_x,1);
        return (0);
    }
    通过这个小小的修改,g_x就能以原子操作方式来递增,因此可以确保g_x中的值最后是2。当然楼主用的关键代码段也是一种线程同步的方法。
      

  5.   

    建议lz看下win32多线程程序设计,要不别人说了,你还是比较模糊的。理解了,你要有一个并行思维去考虑问题,也就慢慢的理解多线程运行的本质了。
      

  6.   

    这种一读一写的非类对象没必要加锁,而且加了也没意义。能读到脏数据是必然的。锁的作用是 在锁有效期内 不希望其他线程对你的资源有操作。
    自己权衡下如果同时有多个线程对其进行操作会有什么样的结果。在你所顾虑的场景中,只有两种可能 A在读的时候中断,B写。B写的时候中断 A读。
    线程A读 g_Some,读到一半被中断,线程B改了g_Some,这种情况我们说 A读到了脏数据。
    如果加锁 A读g_Some,B尝试修改。B被中断,A读完g_Some,B恢复 修改 g_Some,这样A读到的依然是B修改前的。加锁是有代价的,不是无偿的。自己模拟下运行的整个过程,权衡得失,你才能把控多线程的本质。而不是一味的去问。
      

  7.   

    应该考虑int g_Some在汇编语言中会不会翻译成一句,如果是一句,就不需要加锁。如果不是一句就需要加锁。