Delphi多线程编程(14)--多线程同步之WaitableTimer(等待定时器对象)

转载自:万一的博客

function CreateWaitableTimer(
    lpTimerAttributes: PSecurityAttributes;    //安全
    bManualReset: BOOL;        //True:可调度多个线程;False:只调度一个线程
    lpTimerName: PWideChar        //名称
):THandle; stdcall;    //返回句柄

function SetWaitableTimer(
    hTime: THandle;        //句柄
    var lpDueTime: TLargeInteger;    //起始时间
    lPeriod: Longint;        //间隔时间
    pfnCompletionRoutine: TFNTimerAPCRoutine;    //回调函数的指针
    lpArgToCompletionRoutine: Pointer;    //给回调函数的参数
    fResume: BOOL;    //是否唤醒系统
): BOOL; stdcall;

  WaitableTimer 对象比较复杂,其基本的理念是让等待的线程在指定的时间运行

  像其他同类对象一样,要先建立(CreateWaitableTimer),建立函数的第二个参数决定是调度一个线程还是所有等待的线程;这一点和信号对象(Semaphore)有些类似,不过Semaphore可以指定可驱动线程的具体数目

  和其他同类对象不同的是:在CreateWaitableTimer 以后,WaitableTimer 对象并没有马上开始工作

  再调度SetWaitableTimer函数之后才能让它发挥作用,这又有点像Event 对象

 



  SetWaitableTimer 函数比较麻烦,得慢慢来,譬如这样使用

var
    hWaitableTimer: THandle;    //WaitableTimer 对象的句柄变量应该是全局的

procedure TForm1.Button1Click(Sender: TObject);
var
    DueTime: Int64;
begin
    {建立 WaitableTimer 对象并返回句柄}
    hWaitableTimer:= CreateWaitableTimer(nil. True, nil);    //中间的True表示可驱动多个线程
    DueTime:= 0;        //这将是SetWaitableTimer的第二个参数;因为是var参数,不能直接给常量
    SetWaitableTimer(hWaitableTimer,    //WaitableTimer 对象的句柄
                                DueTime,             //起始时间,这里给的是0
                                0,                        //间隔时间,这里给的也是0
                                nil,                       //暂时不用回调函数
                                nil,                       //当然也就不需要回调函数参数了
                                False                    //此值若是True,即使系统在屏保或待机状态,时间一到线程和系统将都被唤醒
                                );
end;

  再说明:

  起始时间(第二个参数)有三种赋值方法:

1) >0 时是绝对时间,是一个 TFileTime 格式的时间(具体赋值方法后面详解)

2) <0 时是相对时间,相对是相对当前,譬如 -50000000 表示5 秒钟后执行(单位是0.1毫秒,后面详述)

3) =0 时,立即执行,不用等待;上面的举例和下面的第一个例子我们先用 0

  间隔时间(第三个参数)有两种情况:

1) 譬如5000 表示每隔5 秒钟执行一次,其单位是毫秒;本页第二个例子使用了 500(半秒)

2) 如果赋值为0, 表示根据起始时间只执行一次,不再重复执行

  回调函数及其参数(第四、五个参数),这会牵扯出一个更复杂的话题(APC),暂时不用它,后面再说

  最后一个参数是最后一个参数上面已经说清楚了, 我也测试了一下(分别在屏保和待机状态下), 很有效!



  第一个例子我们将尽量简单的使用它(但是这样体现不出它的优势):

CreateWaitableTimer 时我们就决定让它可控制多个线程

SetWaitableTimer 时先让它立即参与控制,只执行一次,也不使用回调函数

  本例效果图

  代码文件

unit Unit1;

interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

var
  f: Integer;
  hWaitableTimer: THandle;    //等待定时器对象的句柄

function MyThreadFun(p: Pointer); DWORD; stdcall;
var
  i, y: Integer;
begin
  Inc(f);
  y:= 20*f;
  if WaitForSingleObject(hWaitableTimer, INFINITE)=WAIT_OBJECT_0 then
  begin
    for i:=0 to 1000 do
    begin
      Form1.Canvas.Lock;
      Form1.Canvas.TextOut(20, y, IntToStr(i));
      Form1.Canvas.UnLock;
      Sleep(1);
    end;
  end;
  Result:= 0;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  ThreadID: DWORD;
  DueTime: Int64;
begin
  hWaitableTimer:= CreateWaitableTimer(nil, True, nil);
  DueTime:= 0;
  SetWaitableTimer(hWaitableTimer, DueTime, 0, nil, nil, False);

  Repaint; f:=0;
  CreateThread(nil, 0, @MyThreadFun, nil, 0, ThreadID);
  CreateThread(nil, 0, @MyThreadFun, nil, 0, ThreadID);
  CreateThread(nil, 0, @MyThreadFun, nil, 0, ThreadID);
end;

procedure TForm1.FormDestory(Sender: TObject);
begin
  CloseHandle(hWaitableTimer);    //对于内核对象句柄,一定要关闭
end;

end.

  窗体文件

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = ‘Form1‘
  ClientHeight = 116
  ClientWidth = 179
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = ‘Tahoma‘
  Font.Style = []
  OldCreateOrder = False
  OnDestroy = FormDestroy
  PixelsPerInch = 96
  TextHeight = 13
  object Button1: TButton
    Left = 96
    Top = 83
    Width = 75
    Height = 25
    Caption = ‘Button1‘
    TabOrder = 0
    OnClick = Button1Click
  end
end

  



  下面是一个每隔半秒钟(5000ms) 执行一次的例子(窗体文件同上):

  本例效果图

  代码文件

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

var
  f: Integer;
  hWaitableTimer: THandle;

function MyThreadFun(p: Pointer): DWORD; stdcall;
var
  i, y: Integer;
begin
  Inc(f);
  y:= 20*f;

  {这里和上面不同,把等待农贷循环里面了}
  for i:=  0 to 1000 do
  begin
    if WaitForSingleObject(hWaitableTimer, INFINITE)= WAIT_OBJECT_0 then
    begin
      Form1.Canvas.Lock;
      Form1.Canvas.TextOut(20, y, IntToStr(i));
      Form1.Canvas.Unlock;
//      Sleep(1);
    end;
  end;
  Result := 0;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  ThreadID: DWORD;
  DueTime: Int64;
begin
  hWaitableTimer := CreateWaitableTimer(nil, False, nil); {这里的参数也和上面不一样}
  DueTime := 0;
  SetWaitableTimer(hWaitableTimer, DueTime, 500, nil, nil, False); {500 ms}

  Repaint; f := 0;
  CreateThread(nil, 0, @MyThreadFun, nil, 0, ThreadID);
  CreateThread(nil, 0, @MyThreadFun, nil, 0, ThreadID);
  CreateThread(nil, 0, @MyThreadFun, nil, 0, ThreadID);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  CloseHandle(hWaitableTimer);
end;

end.

  

时间: 2024-10-11 20:09:29

Delphi多线程编程(14)--多线程同步之WaitableTimer(等待定时器对象)的相关文章

Linux程序设计学习笔记----多线程编程之线程同步之条件变量

转载请注明出处:http://blog.csdn.net/suool/article/details/38582521. 基本概念与原理 互斥锁能够解决资源的互斥访问,但是在某些情况下,互斥并不能解决问题,比如两个线程需 要互斥的处理各自的操作,但是一个线程的操作仅仅存在一种条件成立的情况下执行,一旦错过不可再重现,由于线程间相互争夺cpu资源,因此在条件成立的时候,该线程不一定争夺到cpu而错过,导致永远得不到执行..... 因此需要某个机制来解决此问题,更重要的是,线程仅仅只有一种情况需要执

.NET面试题解析(07)-多线程编程与线程同步

系列文章目录地址: .NET面试题解析(00)-开篇来谈谈面试 & 系列文章索引 关于线程的知识点其实是很多的,比如多线程编程.线程上下文.异步编程.线程同步构造.GUI的跨线程访问等等,本文只是从常见面试题的角度(也是开发过程中常用)去深入浅出线程相关的知识.如果想要系统的学习多线程,没有捷径的,也不要偷懒,还是去看专业书籍的比较好. 常见面试题目: 1. 描述线程与进程的区别? 2. 为什么GUI不支持跨线程访问控件?一般如何解决这个问题? 3. 简述后台线程和前台线程的区别? 4. 说说常

Linux多线程编程——线程的同步与互斥

前言:无论是多线程编程还是多进程编程,控制好不同线程或不同进程之间同步和互斥问题是非常有必要的.同步是多个进程或线程共同完成某个任务,举例说,一个缓冲区的生产者和消费者问题,当生产者生产了一个商品时,等待的消费者就获得了一个消息知道可以去取走商品了,当消费者取走一个商品后,生产者就知道可以继续生产一个商品了,这是同步问题,所谓互斥问题,是指某个共享资源在一次操作中,只能被一个线程或进程占有,其他的线程或进程不能对它进行操作,比如对一个共享内存的读写操作,当一个进程对它写的时候,另一个进程就不能对

Qt多线程编程总结(一)(所有GUI对象都是线程不安全的)

Qt对线程提供了支持,基本形式有独立于平台的线程类.线程安全方式的事件传递和一个全局Qt库互斥量允许你可以从不同的线程调用Qt方法. 这个文档是提供给那些对多线程编程有丰富的知识和经验的听众的.推荐阅读: Threads Primer: A Guide to Multithreaded Programming Thread Time: The Multithreaded Programming Guide Pthreads Programming: A POSIX Standard for Be

Delphi多线程编程(15)--多线程同步之WaitableTimer(等待定时器对象)[续]

转载自:万一的博客 本次专门研究下 SetWaitableTimer的第二个参数(起始时间) 它有正值.负值.0值这三种情况,前面已经用过 0 值 先学习负值(相对时间),也就是从当前算起隔多长时间开始执行 这个相对时间是以 1/100 纳秒为单位的,譬如赋值:3*10000000 相当于 3 秒 1 s(秒) = 1,000            ms(毫秒); 1 s(秒) = 1,000,000        µs(微妙); 1 s(秒) = 1,000,000,000    ns(纳秒)

Delphi多线程编程(8)--多线程同步之CriticalSection(临界区)

转载自:万一的博客 先看一段程序 unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1=class(TForm) ListBox1: TListBox; Button1: TButton; procedure FormCreate(Sender: TObject); procedure

Delphi中线程类TThread实现多线程编程(线程同步技术、Synchronize、WaitFor……)

接着上文介绍TThread. 现在开始说明 Synchronize和WaitFor 但是在介绍这两个函数之前,需要先介绍另外两个线程同步技术:事件和临界区 事件(Event)与Delphi中的事件有所不同.从本质上讲,Event其实就相当于一个全局的布尔变量.它有两个赋值操作:Set和ReSet,相当于把它设置为 True或False.而检查它的值是通过WaitFor操作进行.对应在Windows平台上,是三个API函数:SetEvent.ResetEvent.WaitForSignalObje

Delphi多线程编程(12)--多线程同步之Semaphore(信号对象)

转载自:万一的博客 之前已经有了两种线程同步的方法: CriticalSection(临界区)和Mutex(互斥)吗,这两种同步方法差不多,只是作用域不同 CriticalSection类似于只有一个蹲位的公共厕所,只能一个个地进 Mutex 对象类似于接力赛中的接力棒,某一时刻只能有一个人持有,谁拿着谁跑 什么是Semaphore(信号或叫信号量)呢? 譬如到银行办业务.或者到车站买票,原来只有一个服务员,不管有多少人排队等候,业务只能一个个地来 假如增加业务窗口,可以同时受理几个业务呢? 这

Delphi多线程编程(10)--多线程同步之Mutex(互斥对象)

原理分析: 互斥对象是系统内核对象,各个线程都可以拥有它,谁拥有它谁就可以执行 执行完毕,用ReleaseMutex 函数释放拥有权,以让其他等待的线程可以使用 其他线程可以使用 WaitForSingleObject函数排队等待(等待也可以理解为排队申请) 使用过程 var hMutex: THandle; {应该先声明一个全局的互斥句柄} CreateMutex {建立互斥对象} WaitForSingleObject {用等待函数排队等候} ReleaseMutex {释放拥有权} Clo