3. 线程中常见的问题。

1) 回调函数引起的死锁。

A回调线程B中的函数,而在线程B中,再去对线程A进行操作(比如删除A)。发生的现象:程序死掉。

2) 使用同一资源未加保护引起问题。

A和B同时去对窗体上进行绘图操作,界面可能花掉,也可能黑掉。

出现的现象:界面不再刷新,变成黑色。(最好不要在子线程中去更新界面UI,可以使用消息来更新)

3) 线程锁使用不当造成死锁。

线程A利用线程锁锁住资源A后,再去试图访问资源B,线程B利用线程锁锁住资源B后试图去访问资源A。这样就发生了线程互锁。

程序结果:线程死掉。

4) 未加线程保护产生异常。

线程A获取到了对象X(步骤1)的引用后被挂起(步骤2),而接下来线程B却删除了X(步骤3),线程A再次唤醒后访问对象X出错(步骤4)。这个问题是多线程中最容易被忽略的地方,也是异常最可能发生的情况。

程序结果:线程异常。

5) 消息在线程同步中的问题。

先说说消息的一些基本问题(有关消息的处理部分在Windows 2000源码private\ntos\w32\ntuser\kernel\input.c文件中):

消息队列的建立:线程在刚建立的时候,是没有消息队列的。当有界面UI操作函数被调用的时候(比如CreateWindow),Windows就会为该程序建立一个消息队列,同样,通过调用PeekMessage/GetMessage可以强制操作系统为线程建立一个消息队列(参看MSDN关于PostThreadMessage的说明)。

消息的正常处理流程:线程通过GetMessage/PostMessage获取消息,然后通过TranslateMessage进行字符转换,接下来,通过User32.dll模块的帮助最后调用到相应窗口的窗口过程(RegisterClass时传入的窗口过程)。

消息重入问题:所谓的消息重入,就是在消息处理过程中再调用SendMessage发送消息给目标窗口。如果控制不好,就会引发异常。

一个简单的例子:在WM_PAINT中再去SendMessage(hWin, WM_PAINT, 0, 0)。其结果就是堆栈溢出异常了(stack overflow)。(相当于WndProc在进行无限递归),Delphi代码如下(可以自己感受一下“Stack Overflow”是怎么一回事,等异常后调出CallStack看看^_^):

procedure OnPaint(var tMsg: TMessage); message WM_PAINT;                 // Interface
// Implementation
procedure TForm1.OnPaint(var tMsg: TMessage);
begin
  SendMessage(Self.Handle, WM_PAINT, 0, 0);
end;

SendMessage & PostMessage:

SendMessage发送一个消息给窗口,同时,操作系统会去直接调用窗口的窗口过程而不经过线程的消息队列。

而PostMessage则仅仅是将消息投递到消息队列,应用程序通过GetMessage/PeekMessage获取消息处理后再交给系统分发消息。

消息在多线程中的问题:

分析一个具体过程说明在多线程中因消息而引起的问题:

线程A(主线程)通过GetMessage->DispatchMessage,接下来通过User32模块的帮助调用WndProc进行消息处理的过程对RichEdit中插入一张图片。其步骤如下:

1. 在RichEdit中定位要插入的位置(X, Y)。RichEdit->SetSel(X, Y)
                2. 创建OLE对象。

3. 获取ClientSite接口插入对象。

线程B通过SendMessage调用WndProc要求在RichEdit的末尾添加一段文字。其步骤如下:

1. 定位到RichEdit末尾。RichEdit->SetSel(-1, -1);
               2. 调用RichEdit->ReplaceSel(sText)插入文本。

假如线程A在步骤1刚运行完毕后,其运行时间片结束,线程被挂起,当前的状态被保存到线程A的堆栈中。

线程B开始运行,线程B插入文本完成返回,线程A重新被唤醒,开始执行步骤2,3, 而这时,线程B已经改变了当前的插入位置,线程A并不知道,于是,就会出现插入的图片错位现象。归根结底,是线程的同步问题。结论:

尽量用PostMessage而不是SendMessage。

6) 异常引起的问题。

看这段代码:

CCriticalSection  m_cLock;
  
   m_cLock.Lock;
   // Do something
   m_cLock.Unlock();
 
    初看是没有什么问题,但是,如果我们在DoSomeThing的时候产生了异常,那么UnLock代码将不会被执行,于是,线程锁将一直处与加锁状态,其他线程将无法访问。

m_cLock.Lock;
try
{
         // Do something
         m_cLock.Unlock();
}
catch(…)
{
m_cLock.Unlock();
}
 
在Delphi中处理这种情况很简单:m_cLock.Lock;
try
  // Do something
finally
 m_cLock.Unlock;
end;
 
以上是我在多线程中所遇到的一些问题的总结,希望能对大家有用。

4、线程效率

虽然线程能够提高我们的程序的效率,然而,如果使用不当,反而会降低程序效率。特别是在线程同步的过程中,对于公共资源的读写保护部分。

看下面的例子(假设这是一个网络多线程下载程序,一个线程负责将下载的内容保存到文件,另外几个线程负责将数据加到队列中,下面写出保存线程的示例):

typedef struct
{
   char           m_pBuf[1024];           // … Data
}TNode, *PNode;
 
private:
   CPtrList                                m_plTmpList;
CCriticalSection                 m_cLock;
 
 POSITION       posTmp;
          PNode             pItem = NULL;
 
m_cLock.Lock();
try
{
   posTmp = m_plTmpList.GetTailPosition();
 
   while(posTmp)
   {
     posPrev = posTmp;
         pItem = (PNode)m_plTmpList.GetPrev(posTmp);
      // 对于pItem进行处理,保存到磁盘      m_plTmp.RemoveAt(posPrev);
     delete pItem;
   }
  m_cLock.Unlock();
}
catch(…)
{
       m_cLock.Unlock();
}

通常情况下,我们会使用这种方式来遍历整个列表,然而,如果我们对于pItem的处理需要很长时间,比如我们要将pItem中的数据存放到硬盘上,那么这个过程将会非常耗时。其它线程将无法访问该列表。

那么如何才能提高效率呢?

我们可以使用两个队列,一个队列设置为下载队列,另外一个是当前保存队列。当保存队列中的所有内容全部保存到磁盘后,我们将下载队列和保存队列进行交换,即下载队列变成保存队列,保存队列变成下载队列。

总之,在线程同步操作中,提高线程效率最重要的就是减少线程公共资源操作的时间,或者是采用其它方法避免同步。

5、后记(题外话)

当我们习惯于Windows下的RAD开发工具的时候,我们往往忽略了对于整个RDA环境的封装以及系统底层的探究,隐藏在操作系统内部的东西或机制往往被我们忽略。

RAD工具大大提高了开发效率,然后它也助长了我们的惰性。

很多人在抱怨大学里学习的东西都没有用,然而,现在看来,当初的很多东西都是很有用的,比如操作系统原理,数据结构和算法。

我不是计算机的专业出身,我很庆幸当初自己对哪些东西略微了解了一些,以至于我现在理解起来一些东西不再那么困难。

理解Windows的运行原理,Windows的主要模块作用,对于软件开发有很大的帮助。

很长一段时间里,我被Windows的华丽外衣所迷惑,整天还沉浸在DOS下的单任务环境,迷失在Windows下的软件开发中。幸好,现在终于走出了这片森林,看到了森林的一角。

可以欣慰的说一声:我终于找到进入软件开发的大门了。软件开发,是人和机器的交互过程。

我们想让机器更好的为我们工作,我们就需要对机器有较多的认识,同时也要对我们所处的开发环境有较多的了解。

软件开发,不仅仅是一门技术,更是一门艺术。然后,在现在,能把它当成一门艺术来看待的人已经不多了

Delphi Thread 多线程编程(6)相关推荐

  1. C++11新特性以及std::thread多线程编程

    一 .C++11新特性 1. auto 类型推导 1.1 当=号右边的表达式是一个引用类型时,auto会把引用抛弃,直接推导出原始类型: 1.2 当=号右边的表达式带有const属性时,auto不会使 ...

  2. delphi的多线程编程

    多线程的基本概念 (转) win 98/nt/2000/xp 是个多任务操作系统,也就是:一个进程可以划分为多个线程, 每个线程轮流占用cpu 运行时间和资源,或者说,把cpu 时间划成片,每个片分给 ...

  3. Delphi多线程编程基础入门

    1. 概述 对于开发人员来说,多线程是必备的知识,但相对来说,也是比较难的知识点.Delphi是一门古老而优秀的编程语言,它对多线程的处理有一些特殊的地方,本文尝试做一些简单的讲解,可以当作Delph ...

  4. C 多线程编程之在类中使用多线程(thread)的方法

    一.thread的基本用法 参见C++使用thread类多线程编程 . 二.类外使用多线程,访问类的成员 这几种方式,新建线程都是在类外,然后通过把友元函数或者成员函数作为thread参数. #inc ...

  5. C++使用thread类多线程编程

    目录 pthread多线程 系统自带CreateThread std::thread c++ 多线程总结_jacke121的专栏-CSDN博客 std thread比较好用,但是系统带的socket不 ...

  6. C#多线程编程介绍——使用thread、threadpool、timer

    C#多线程编程介绍--使用thread.threadpool.timer 在system.threading 命名空间提供一些使得能进行多线程编程的类和接口,其中线程的创建有以下三种方法:thread ...

  7. c# 多线程 执行事件 并发_C#.NET Thread多线程并发编程学习与常见面试题解析-1、Thread使用与控制基础...

    前言: 因为平时挺少用到多线程的,写游戏时都在用协程,至于协程那是另一个话题了,除了第一次学习多线程时和以前某个小项目有过就挺少有接触了,最近准备面试又怕被问的深入,所以就赶紧补补多线程基础. 网上已 ...

  8. C#.NET Thread多线程并发编程学习与常见面试题解析-1、Thread使用与控制基础

    前言: 因为平时挺少用到多线程的,写游戏时都在用协程,至于协程那是另一个话题了,除了第一次学习多线程时和以前某个小项目有过就挺少有接触了,最近准备面试又怕被问的深入,所以就赶紧补补多线程基础. 网上已 ...

  9. Java多线程编程:变量共享分析(Thread)

    Java多线程编程:变量共享分析(Thread) Java 创建线程的两种方法 此处只简单讲下自己对java多线程变量共享的理解: 按照进程和多线程的原理,同一进程内的多个线程之间的地址空间是共享的( ...

最新文章

  1. opencv3.2 在Ubuntu下的编译安装
  2. xampp配置xdebug
  3. 网页的一般布局(标题和脚注100%,内容宽度固定宽度px)
  4. 大数据可视化html模板开源_5个最受工程师欢迎的大数据可视化工具
  5. qt5 linux 控制台 乱码,qt5.12 解决显示中文乱码问题
  6. fastreport 中 给数值形数据做 格式 保留小数位数以0补足
  7. 微软应用商店_微软自家的软件也放弃Windows 10
  8. U3D激发拍照新活力,Camera360优化之旅
  9. es6 作为对象属性的Generator函数
  10. 《视觉SLAM十四讲》课后习题—ch7(更新中……)
  11. 分支限界算法c语言_算法分析
  12. 今年立下的 Flag 倒了几个?
  13. ~~核心编程(三):面向对象——逻辑交互与组合~~
  14. ai/ml_您应该在本周(7月11日)阅读有趣的AI / ML文章
  15. 光猫的ip地址段和路由器ip地址段互换
  16. 【TensorFlow】tf.expand_dims()函数
  17. 扫地机器人单扫和双扫_扫地机器人初体验 懒人神器还是逗猫的塑料玩具
  18. miui9免解锁root,Miui解锁
  19. 神经网络激活函数及其Katex公式代码模板合集
  20. linux下运行mcnp6安装教程,JBPM6教程-手把手教你安装JBPM

热门文章

  1. Python小学生课程学哪些内容?
  2. C基础 | 【05】(内存结构以及复合类型)
  3. 欢迎大家关注知识分享公众号
  4. docker最全笔记速查,逻辑清晰
  5. 空气开关A型、B型、C型、D型的区别
  6. SAP中内部订单状态导致不能收货处理实例
  7. Java学生信息管理系统——管理员登录模块(简单易上手)
  8. 错误使用 load ASCII 文件的行号 1 中的文本未知“MATLAB“。
  9. 设置mysql回收空闲链接_mysql 8小时空闲后连接失效的解决
  10. 常用快捷键cmd常用dos命令java安装