delphi多线程互斥,用多线程怎么解决同一时间内调用同一函数
Delphi同步互斥总结多个线程同时访问一个共享资源或数据时,需要考虑线程同步,Synchronize()是在一个隐蔽的窗口里运行,如果在这里你的任务很繁忙,你的主窗口会阻塞掉;Synchronize()只是将该线程的代码放到主线程中运行,并非线程同步。 临 界区是一个进程里的所有线程同步的最好办法,他不是系统级的,只是进程级的,也就是说他可能利用进程内的一些标志来保证该进程内的线程同步,据 Richter说是一个记数循环;临界区只能在同一进程内使用;临界区只能无限期等待,不过2k增加了TryEnterCriticalSection函 数实现0时间等待。 互斥则是保证多进程间的线程同步,他是利用系统内核对象来保证同步的。由于系统内核对象可以是有名字的,因此多个 进程间可以利用这个有名字的内核对象保证系统资源的线程安全性。互斥量是Win32 内核对象,由操作系统负责管理;互斥量可以使用WaitForSingleObject实现无限等待,0时间等待和任意时间等待。常见的线程同步方法如下:1. 临界区临界区是一种最直接的线程同步方式。所谓临界区,就是一次只能由一个线程来执行的一段代码。如果把初始化数组的代码放在临界区内,另一个线程在第一个线程处理完之前是不会被执行的。使用方法如下://在窗体创建中InitializeCriticalSection(Critical1)//在窗体销毁中DeleteCriticalSection(Critical1)//在线程中EnterCriticalSection(Critical1)……保护的代码LeaveCriticalSection(Critical1)2. 互斥互斥非常类似于临界区,除了两个关键的区别:首先,互斥可用于跨进程的线程同步。其次,互斥能被赋予一个字符串名字,并且通过引用此名字创建现有互斥对象的附加句柄。 临界区与事件对象(比如互斥对象)的最大的区别是在性能上。临界区在没有线程冲突时,要用10 ~ 15个时间片,而事件对象由于涉及到系统内核要用400~600个时间片。Mutex(互斥对象),是用于串行化访问资源的全局对象。我们首先设置互斥对象,然后访问资源,最后释放互斥对象。在设置互斥对象时,如果另一个线程(或进程)试图设置相同的互斥对象,该线程将会停下来,直到前一个线程(或进程)释放该互斥对象为止。注意它可以由不同应用程序共享。使用方法如下://在窗体创建中hMutex:=CreateMutex(nil,false,nil)//在窗体销毁中CloseHandle(hMutex)//在线程中WaitForSingleObject(hMutex,INFINITE)……保护的代码ReleaseMutex(hMutex)3. 信号量另一种使线程同步的技术是使用信号量对象。它是在互斥的基础上建立的,但信号量增加了资源计数的功能,预定数目的线程允许同时进入要同步的代码。可以用CreateSemaphore()来创建一个信号量对象, 因为只允许一个线程进入要同步的代码,所以信号量的最大计数值(lMaximumCount)要设为1。其实Mutex就是最大计数为一的Semaphore。使用方法如下://在窗体创建中hSemaphore:= CreateSemaphore(nil,lInitialCount,lMaximumCount,lpName)//在窗体销毁中CloseHandle(hSemaphore)//在线程中WaitForSingleObject(hSemaphore,INFINITE)……保护的代码ReleaseSemaphore(hSemaphore, lReleaseCount, lpPreviousCount)4.WaitForSingleObject函数的返值:WAIT_ABANDONED指定的对象是互斥对象,并且拥有这个互斥对象的线程在没有释放此对象之前就已终止。此时就称互斥对象被抛弃。这种情况下,这个互斥对象归当前线程所有,并把它设为非发信号状态;WAIT_OBJECT_0 指定的对象处于发信号状态;WAIT_TIMEOUT等待的时间已过,对象仍然是非发信号状态;Delphi 常用的临界区对象TCriticalSection(Delphi) 、TRtlCriticalSectionTRtlCriticalSection 是一个结构体,在windows单元中定义; 是InitializeCriticalSection,EnterCriticalSection,LeaveCriticalSection, DeleteCriticalSection 等这几个kernel32.dll中的临界区操作API的参数;TCriticalSection是在SyncObjs单元中实现的类,它对上面的那些临界区操作API函数进行了了封装,简化并方便了在Delphi的使用;如TCriticalSection.Create,TCriticalSection.Enter, TcriticalSection.Leave等;通过调用上面响应的API函数实现。线程同步的多种办法中,使用临界区最简单,也是效率最高的办法(CPU占用时间最少)使用临界区代码如下:先声明一个TRTLCriticalSection类型的全局变量varMyCs:TRTLCriticalSection;在程序开始或建立线程之前,初始化InitializeCriticalSection(MyCs);//初始化临界区在程序结束或所有线程结束后,删除它DeleteCriticalSection(MyCs);//删除临界区再在线程中要同步的地方加入EnterCriticalSection(MyCs); //进入临界区try//程序代码finallyLeaveCriticalSection(MyCs); //离开临界区end;补充今天遇到的关于Application.ProcessMessages同步的问题:有一个函数Fn按执行顺序可分为A->B->C 3大块,其中B块有要绘制各种窗口界面的操作很复杂且耗时较长,并且里面用到了Application.ProcessMessages,程序运行测试时发现如果在Fn执行B绘制窗口的过程没结束时又调用Fn函数去绘制其它窗口就可能会导致程序崩溃,一开始尝试用TcriticalSection变量解决,完全没用,最后用增加一个全局变量的方法解决:定义一个全局Boolean型变量flag,设定初始值为True,改造Fn函数的逻辑为A-> if flag thenBeginFlag:=False;B;Flag:=True;End;->C问题成功解决。顺便总结Application.ProcessMessages的作用:运行一个非常耗时的循环,那么在这个循环结束前,程序可能不会响应任何事件,按钮没有反应,程序设置无法绘制窗体,看上去就如同死了一样,这有时不是很方便,例如于终止循环的机会都没有了,又不想使用多线程时,这时你就可以在循环中加上这么一句,每次程序运行到这句时,程序就会让系统响应一下消息,从而使你有机会按按钮,窗体有机会绘制。所起作用类似于VB中DoEvent方法. 调用ProcessMessages来使应用程序处于消息队列能够进行消息处理,ProcessMessages将Windows消息进行循环轮转,直至消息为空,然后将控制返回给应用程序。注示:仅在应用程序调用ProcessMessages时勿略消息进程效果,而并非在其他应用程序中。在冗长的操作中,调用ProcessMessages周期性使得应用程序对画笔或其他信息产生回应。 ProcessMessages不充许应该程序空闲,而HandleMessage则然.使用ProcessMessages一定要保证相关代码是可重入的,如果实在不行也可按我上面的方法实现同步。
Windows多线程编程实现数据互斥访问的几个方法
临界区(Critical Section):适合一个进程内的多线程访问公共区域或代码段时使用互斥量 (Mutex):适合不同进程内多线程访问公共区域或代码段时使用,与临界区相似。事件(Event):通过线程间触发事件实现同步互斥信号量(Semaphore):与临界区和互斥量不同,可以实现多个线程同时访问公共区域数据,原理与操作系统中PV操作类似,先设置一个访问公共区域的线程最大连接数,每有一个线程访问共享区资源数就减一,直到资源数小于等于零。
什么是多线程编程
多线程编程技术是Java语言的重要特点。多线程编程的含义是将程序任务分成几个并行的子任务。特别是在网络编程中,你会发现很多功能是可以并发执行的。比如网络传输速度较慢、用户输入速度较慢,你可以用两个独立的线程去完成这两个功能,而不影响正常的显示或其它功能。 多线程是与单线程比较而言的,普通的Windows采用单线程程序结构,其工作原理是:主程序有一个消息循环,不断从消息队列中读入消息来决定下一步所要干的事情,一般是针对一个函数,只有等这个函数执行完之后,主程序才能接收另外的消息来执行。比如子函数功能是在读一个网络数据,或读一个文件,只有等读完这个数据或文件才能接收下一个消息。在执行这个子函数过程中你什么也不能干。但往往读网络数据和等待用户输入有很多时间处于等待状态,多线程利用这个特点将任务分成多个并发任务后,就可以解决这个问题。 Java中的线程类 1.扩展java.lang.Thread类,用它覆盖Thread类的run方法。 2.生成实现java.lang.Runnable接口的类并将其它的实例与java.lang.Thread实例相关联。 Thread类是负责向其它类提供线程支持的最主要的类,要使用一个类具有线程功能,在Java中只要简单地从Thread类派生一个子类就可以了扩展Thread类,如printThread.java。 Thread类最重要的方法是run方法。run方法是新线程执行的方法,因此生成java.lang.Thread的子类时,必须有相应的run方法。//PrintThread.javapublic class PrintThread extends Thread//继承Tread类private int count=0//定义一个count变量用于统计打印的次数并共享变量public static void mainString args//main方法开始PrintThread p=new PrintThread//创建一个线程实例p.start//执行线程for{;;}//主线程main方法执行一个循环,for执行一个死循环count++System.out.printcount+″:Main\n″//主线程中打印count+“main”变量的值,并换行public void run//线程类必须有的run()方法for{;;}count++System.out.printcount+″:Thread\n″ 上面这段程序便是继承java.lang.Tread并覆盖run的方法。用Java虚拟机启动程序时,这个程序会先生成一个线程并调用程序主类的main方法。这个程序中的main方法生成新线程,连接打印“Thread”。在启动线程之后,主线程继续打印“Main”。 编译并执行这个程序,然后立即按“Ctrl+C”键中断程序,你会看到上面所述的两个线程不断打印出:XXX:main…..XXX:Thread….XXX代表的是数字,也就是上面count的值。在笔者的机器上,不同时刻这两个线程打印的次数不一样,先打印20个main(也就是先执行20次主线程)再打印出50次Thread,然后再打印main…… 提示:为了便于查看该程序的执行结果,你可以将执行结果导入一个文本文件,然后打开这个文件查看各线程执行的情况。如运行:javac PrintThread.javaJava PrintThread1.txt 第一个命令javacPrintThread.java是编译java程序,第二个是执行该程序并将结果导入1.txt文件。当然你可以直接执行命令:java
什么是多线程编程?什么时候使用
多线程的使用主要是用来处理程序“在一部分上会阻塞”,“在另一部分上需要持续运行”的场合。一般是根据需求,可以用多线程,事件触发,callback等方法达到。但是有一些方法是只有多线程能办到的就只有用多线程或者多进程来完成。
举个简单的例子,能理解就行。假设有这样一个程序,
1会不停的处理收到的所有TCP请求。对于每个TCP请求做不同的操作。不能有遗漏
2有很多特定的请求会向一个服务器发送存储的数据,或者是等待用户输入。
来看看。第1个要求很简单。用个while循环就搞定了。但第2个特性呢。一旦在等待用户输入或者是连接服务器时,程序会“阻塞”一段时间,这一段时间内就无法处理其他的TCP请求了。
所以可以利用多线程,每个线程处理不同的TCP请求。这样程序就不会“阻塞”掉了。