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
    >>像这样的操作,本身是一种错误,子线程的存在就是为了让主线程不进行等待。

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

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

  • 相关阅读:
    泛微云桥e-Bridge 目录遍历,任意文件读取
    (CVE-2020-8209)XenMobile-控制台存在任意文件读取漏洞
    selenium 使用初
    将HTML文件转换为MD文件
    Python对word文档进行操作
    使用java安装jar包出错,提示不是有效的JDK java主目录
    Windows server 2012安装VM tools异常解决办法
    ifconfig 命令,改变主机名,改DNS hosts、关闭selinux firewalld netfilter 、防火墙iptables规则
    iostat iotop 查看硬盘的读写、 free 查看内存的命令 、netstat 命令查看网络、tcpdump 命令
    使用w uptime vmstat top sar nload 等命令查看系统负载
  • 原文地址:https://www.cnblogs.com/railgunman/p/1869495.html
Copyright © 2011-2022 走看看