zoukankan      html  css  js  c++  java
  • 多线程同步

    ★★★关于线程同步:
    Synchronize()是在一个隐蔽的窗口里运行,如果在这里你的任务很繁忙,你的主窗口 会阻塞掉;Synchronize()只是将该线程的代码放到主线程中运行,并非线程同步。
    临界区是一个进程里的所有线程同步的最好办法,他不是系统级的,只是进程级的,也就是说他可能利用进程内的一些标志来保证该进程内的线程同步,据Richter说是一个记数循环;临界区只能在同一进程内使用;临界区只能无限期等待,不过2k增加了TryEnterCriticalSection函数实现0时间等待。
    互斥则是保证多进程间的线程同步,他是利用系统内核对象来保证同步的。由于系统内核对象可以是有名字的,因此多个进程间可以利用这个有名字的内核对象 保证系统资源的线程安全性。互斥量是Win32 内核对象,由操作系统负责管理;互斥量可以使用WaitForSingleObject实现无限等待,0时间等待和任意时间等待。 
    1. 临界区
    临界区是一种最直接的线程同步方式。所谓临界区,就是一次只能由一个线程来执行的一段代码。如果把初始化数组的代码放在临界区内,另一个线程在第一个线程处理完之前是不会被执行的。在使用临界区之前,必须使用InitializeCriticalSection()过程来初始化它。 在第一个线程调用了EnterCriticalSection()之后,所有别的线程就不能再进入代码块。下一个线程要等第一个线程调用LeaveCriticalSection()后才能被唤醒。
    2. 互斥 
    互斥非常类似于临界区,除了两个关键的区别:首先,互斥可用于跨进程的线程同步。其次,互斥能被赋予一个字符串名字,并且通过引用此名字创建现有互斥对象的附加句柄。 
    提示:临界区与事件对象(比如互斥对象)的最大的区别是在性能上。临界区在没有线程冲突时,要用10 ~ 15个时间片,而事件对象由于涉及到系统内核要用400~600个时间片。 
    当一个互斥对象不再被一个线程所拥有,它就处于发信号状态。此时首先调用WaitForSingleObject()函数的线程就成为该互斥对象的拥有者,此互斥对象设为不发信号状态。当线程调用ReleaseMutex()函数并传递一个互斥对象
    的句柄作为参数时,这种拥有关系就被解除,互斥对象重新进入发信号状态。
    可以调用函数CreateMutex()来创建一个互斥量。
    当使用完互斥对象时,应当调用CloseHandle()来关闭它。
    3. 信号量
    另一种使线程同步的技术是使用信号量对象。它是在互斥的基础上建立的,但信号量增加了资源计数的功能,预定数目的线程允许同时进入要同步的代码。可以用CreateSemaphore()来创建一个信号量对象, 因为只允许一个线程进入要同步的代码,所以信号量的最大计数值(lMaximumCount) 要设为1。ReleaseSemaphore()函数将使信号量对象的计数加1; 记住,最后一定要调用CloseHandle()函数来释放由CreateSemaphore()创建的信号量对象的句柄。
    ★★★WaitForSingleObject函数的返值:
    WAIT_ABANDONED指定的对象是互斥对象,并且拥有这个互斥对象的线程在没有释放此对象之前就已终止。此时就称互斥对象被抛弃。这种情况下,这个互斥对象归当前线程所有,并把它设为非发信号状态;
       WAIT_OBJECT_0 指定的对象处于发信号状态;
       WAIT_TIMEOUT等待的时间已过,对象仍然是非发信号状态;


    问题: 关于线程的简单问题:线程与界面

    来自: painboy, 时间: 2005-03-01 12:58:09, ID: 3001075 
    当有若干个同类线程对界面的同一个VCL控件进行操作进,必须用Synchronize来同步。
    就你的情况来说,设有10个FiveQuery线程进行查询,查询所耗时间约8秒,线程更新界面所需时间大约1秒,这样当你一运行10线程时,前8秒时间界面是可以进行其他操作的,但约8秒后,连续进行10X1即10秒的界面更新,这就会出现10秒左右的“忙沙漏”。
    所以,要避免这情况,一是减少并发运行的线程数;二是缩短SynchronizeSource执行时间。优化你的同步更新过程,同时把尽可能的代码写在Execute里。

    来自: ppqingyu, 时间: 2005-03-01 17:42:45, ID: 3001730 
    呵呵...那像我上面的那些代码还有得救吗?同步的过程全是关于界面更新的呀.
    来自: panbq, 时间: 2005-03-06 0:46:20, ID: 3004732 
    我碰到的问题跟楼主一样,不过把同步部分的代码写到一个线程的EXECUTE里,在更新的时候,创建此更新线程,界面无响应问题就解决了.

    来自: ppqingyu, 时间: 2005-03-06 10:07:03, ID: 3004839 
    不是说界面更新在线程是不是安全的吗?这样不会有问题?

    来自: panbq, 时间: 2005-03-09 19:28:38, ID: 3009426 
    把界面更新的部分在线程里单独写一个过程,如:
    procedure Tprocess_data.show_me;
    begin
    form1.statusbar1.simpletext:=mtt(mtt为线程里的一个局部变量)
    end;
    在线程的执行过程里这样写:
    ...处理数据
    mtt:=要显示的信息
    Synchronize(show_me);

    这样同步过程将非常短,同步只是在主界面显示信息而已,而不是把整个处理过程都写进同步过程中去。。。


    问题: 是否可以不用Synchronize?

    来自: jackchin, 时间: 2004-08-26 11:59:00, ID: 2780104 
    procedure TForm1.ChangeCaption(NewCaption: String);
    begin
       Label1.Caption := NewCaption
    end;
    多个线程调用以上的代码, 是否可以不用Synchronize?
    即, 不用Synchronize, 访问、更新TLabel的属性是否安全?

    来自: 迷糊, 时间: 2004-08-26 12:06:13, ID: 2780123 
    多线程更新TLabel的属性肯定不安全,所以要同步,但是同步可以不用Synchronize
    比如在子线程中发消息通知主线程更新label

    来自: maoyihua, 时间: 2004-08-26 12:23:51, ID: 2780146 
    同意楼上的观点,只要是对于界面有关的操作,都需要使用Synchronize来同步;不过使用消息机制也是一个不错的办法。

    来自: jackchin, 时间: 2004-08-26 14:40:55, ID: 2780427 
    我有一个多线程程序, 偶然会发生不能处理的意外, 估计是线程出问题了
    线程除了上述更新界面的操作外,还有 Getmem, FreeMem, PaintBox.Canvas.Paint等
    另外, 还有数据库操作(用BDE), 我用以下临界段保护的方法
    DBCritical.Enter;
    // here is database access code using TQuery,TTable...
    DBCritical.Leave;
    这些都没有用Synchronize, 不知道是不是有问题

    来自: 迷糊, 时间: 2004-08-26 16:42:23, ID: 2780709 
    这么频繁的界面操作,你还是考虑一下是不是一定要用多线程
    你的临界段保护在主线程中也写了吗?如果没有的话没办法保护。


    问题: 怎么用多线程操作listView。

    来自: 留香客, 时间: 2004-10-19 20:28:00, ID: 2855263 
    我有一个数据库,要把数据全部读入到ListView中。然后进行数据的计算。
    ListView中有两个部分:名称和进度。
    我在线程中,不停查找与当前名称相符的TlistItem,更新进度的值,如果进度为100%,则删除。
    但是即使我用了DoubleBuffer也闪的很厉害,有时候就是一片白色的,还老出错:out of index。我也用BeginUpdate和EndUpdate,就是一片白色。
    大侠帮我看看吧。

    来自: 陷队之士, 时间: 2004-10-19 20:59:55, ID: 2855320 
    我个人认为,之所以能出现你所说的闪烁现象,根本原因不在于你是一个线程,还是几个线程,而在于你处理数据的“场所”。就是说,你的数据如果放在TStringList中处理,处理完后再在ListView中显示出来,可能就会好很多。TStringList与ListView的区别在于,前者只是在背后保存数据、处理数据,而后者不仅要保存数据,更重要的是要显示出来,这就大大限制了速度。
    我们在用某些多文件搜索功能时,会发现,在搜索时,如果你让显示搜索结果的窗口缩得很小,或根本就关闭,这时,它会搜索得非常快,反之,将搜索结果窗口弄得很大,它一边搜索,一边添加显示结果,速度就慢很多。
    我想,这和你的情况是一个道理。

    来自: 刘麻子, 时间: 2004-10-19 21:23:45, ID: 2855359 
    能不能用一个线程负责和界面通讯,其他的线程计算,并把进度发给界面通讯线程.
    ----------------------------
    对拉,这个思路很好,多线程就是这样用的。

    计算线程都Post消息给通讯线程的话,会不会通讯线程忙不过来而漏掉一些消息呢?
    ----------------------------
    漏掉倒不大会,但是可能不及时,界面线程可适当调用Application.ProcessMessages;


    问题: 請教富翁們在多線程中,各個線程能同時去訪問主界面的控件嗎

    来自: gemmy, 时间: 2003-02-18 15:35:00, ID: 1629597 
    比如,每個線程都要訪問主界面的stringGrid控件但不對它進行編寫

    来自: vine, 时间: 2003-02-18 15:45:00, ID: 1629629 
    用synchronize()方法来
    这个方法会做一些工作,把传入的方法交由主线程来执行

    来自: barton, 时间: 2003-02-26 16:02:00, ID: 1645470

    有关线程,我谈点看法。
    对于大多数非可视控件,线程可以并行安全操作。读写Canvas的VCL通过锁定Canvas来实现
    多线程的访问,但是事实上一个线程在访问的时候,另一个线程处于等待状态。实现这种
    访问的控制并不需要通过Synchrinize来实现,而是Canvas自己带一个同步对象,实现重入
    限制。Synchrinize只能保证同类型的多个实例不会同时执行某个过程,并不能实现多个不
    同类线程间的同步控制。
    数据库访问应该尽可能避免在线程中访问。Delphi中的TDataset并不是线程安全的,它对
    线程的限制比VCL还严格。所以大多数数据库引擎中自己带有同步机制,保证多线程间的同
    步。不过,大家知道,应用程序消息循环总是单线程的,基于这个原因,我总是直接通过
    消息来同步,又方便又安全。如果是要求即时应答,线程通过SendMessage向父窗口发送消
    息,否则通过PostMessage向父窗口发送消息,这样,线程通过PeekMessage循环获得父窗
    口返回的消息,必要时还可以在线程中加入事件机制实现线程安全。

    来自: shenjian, 时间: 2003-07-26 16:10:00, ID: 2061692

    非常赞同barton的看法,对界面控件和非界面控件的交互部分最好使用消息驱动的方式,使用多线程访问界面你不是绝对的高手,你是难以驾驭的。而且通常你那样做反而容易破坏程序良好的结构,导致界面部分和下面捆的很紧。


    问题: 使用Synchronize()和使用临界区,互斥对象有何区别?性能差异这么大.为何还要存在互斥同步? ( 积分: 100 )

    来自: 淡淡的笑, 时间: 2002-08-13 10:51:00, ID: 1260767 
    不是指在使用方法上,而是性能及其它细节方面.(书上没有讲到这个,只讲到临界区与事件对象[互斥
    对象]的最大区别是在性能上)

    既然临界区比互斥对象的性能要高40倍左右,为何还要存在互斥同步?哪种情况下(必须)使用互斥同步比较好?

    来自: 张无忌, 时间: 2002-08-13 11:49:00, ID: 1260893 
    Synchronize()是在一个隐蔽的窗口里运行,如果在这里你的任务很繁忙,你的主窗口
    会阻塞掉,我做过测试
    临界区是一个进程里的所有线程同步的最好办法,他不是系统级的,只是进程级的,
       也就是说他可能利用进程内的一些标志来保证该进程内的线程同步,据Richter说
       是一个记数循环。
    互斥则是保证多进程间的线程同步,他是利用系统内核对象来保证同步的。
       由于系统内核对象可以是有名字的,因此多个进程间可以利用这个有名字的内核对象
    保证系统资源的线程安全性


    问题: 一种程序死亡的方式:SendMessage的后果

    主线程和子线程需要交换数据:
    主线程这样运行:
    1。进入临界区
    2。访问全局共享数据
    3。离开临界区

    子线程如下运行:
    1。进入临界区
    2。访问全局共享资源
    3。调用SendMessage发消息给主线程
    4。离开临界区
    5。Sleep(100)
    6。进行循环,直到主线程退出

    这么简单的一个多线程的程序,
    竟然经常的死掉,怀疑是SendMessage造成的
    在线程中调用那个同步的方法也会出现死锁
    因为他也间接调用了SendMessage的方法
    所以,这样的多线程模式,慎用SendMessage

    来自:daxian, 时间:2004-3-17 23:35:06, ID:2508161
    你这么做是找死,SendMessage 的接收消息的窗体的 窗体回调函数 是在主线程内

    而 SendMessage 调用后要等待 消息处理的结束后才返回, 如果这时侯主线程等待进入临街区那么,这就是典型的死锁现象。

    同步方法虽然也间接调用了 SendMessage 但是Delphi作了巧妙的处理而不会进入死锁,你调用会死锁那是你程序上有错误。

    这里你可以用 PostMessage 它不等待消息处理的结束,直接返回

    来自:vonrao, 时间:2004-3-17 23:38:57, ID:2508165
    如果使用Sendmessage,必须等到接受线程线程跳出才返回,这样有可能造成死锁,我有感与你的观点。一般在通知时我使
    用PostMessage或创建一个事件机制,来协调多个线程的同步问题。 

    来自:lich, 时间:2004-3-18 20:47:00, ID:2510034
    设计多线程时,有一个原则,一定要记住,
    就是,锁定的时间尽可能的短,锁定期间做尽可能少的操作,再者,慎用SendMessage 

    来自:daxian, 时间:2004-3-18 22:10:58, ID:2510195
    我现在没有以前版本的Delphi,在那些版本的Delphi用了CM_EXECPROC消息已进行同步然后再主线程内在作相应处理,如下
    function TThread.WaitFor: LongWord;
    var
      Msg: TMsg;
      H: THandle;
    begin
      H := FHandle;
      if GetCurrentThreadID = MainThreadID then
        while MsgWaitForMultipleObjects(1, H, False, INFINITE,
          QS_SENDMESSAGE) = WAIT_OBJECT_0 + 1 do PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE)
      else WaitForSingleObject(H, INFINITE);
      GetExitCodeThread(H, Result);
    end;

    在新版本的Delphi内则使用了事件(Event)和临界区(CriticalSection)的配合来进行同步

    来自:lich, 时间:2004-3-19 19:12:42, ID:2511613
    我又看了一下Classes.pas中TThread类的实现,同时又测试了一下,使用同步方法Synchronize也可能造成死锁

    死锁条件如下:主线程进入等待,等待子线程完成本轮操作,
    子线程此时调用Synchronize方法,而子线程必须等待Synchronize返回后才能继续执行,此时造成死锁

    来自:lich, 时间:2004-3-20 14:03:08, ID:2512394
    >>像这样的操作,本身是一种错误,子线程的存在就是为了让主线程不进行等待。

    世事无绝对,典型以生产者消费者模式为例,主界面为生产者,人工录入,或由主线程接收数据
    交给子线程处理,但是限制了缓冲队列的长度
    这样,当缓冲区不够时,主线程必然需要等待,
    缓冲区空时,子线程等待,子线程有可能通过同步方法,
    将反馈消息显示到主线程的人机界面.,就有可能会出现我所说的
    那种死锁条件了

    如果不限制缓冲的大小,也不会有这种同步问题了
    但是,线程同步也就没有必要讨论了

  • 相关阅读:
    针对web高并发量的处理
    外边距合并,外边距折叠
    cookie 和session 的区别:
    ng-if ng-show ng-hide 的区别
    JavaScript中的arguments,callee,caller
    git常见命令
    jQuery中.bind() .live() .delegate() .on()的区别
    为什么要使用sass
    js兼容性记录
    poj1004
  • 原文地址:https://www.cnblogs.com/railgunman/p/1869495.html
Copyright © 2011-2022 走看看