多线程编程实例,C#多线程编程实例
多线程编程实例,C#多线程编程实例详细介绍
本文目录一览: C#多线程编程实例
问题的提出
所谓单个写入程序/多个阅读程序的线程同步问题 是指任意数量的线程访问共享资源时 写入程序(线程)需要修改共享资源 而阅读程序(线程)需要读取数据 在这个同步问题中 很容易得到下面二个要求
)当一个线程正在写入数据时 其他线程不能写 也不能读
)当一个线程正在读入数据时 其他线程不能写 但能够读
在数据库应用程序环境中经常遇到这样的问题 比如说 有n个最终用户 他们都要同时访问同一个数据库 其中有m个用户要将数据存入数据库 n m个用户要读取数据库中的记录
很显然 在这个环境中 我们不能让两个或两个以上的用户同时更新同一条记录 如果两个或两个以上的用户都试图同时修改同一记录 那么该记录中的信息就会被破坏
我们也不让一个用户更新数据库记录的同时 让另一用户读取记录的内容 因为读取的记录很有可能同时包含了更新和没有更新的信息 也就是说这条记录是无效的记录
实现分析
规定任一线程要对资源进行写或读操作前必须申请锁 根据操作的不同 分为阅读锁和写入锁 操作完成之后应释放相应的锁 将单个写入程序/多个阅读程序的要求改变一下 可以得到如下的形式
一个线程申请阅读锁的成功条件是 当前没有活动的写入线程
一个线程申请写入锁的成功条件是 当前没有任何活动(对锁而言)的线程
因此 为了标志是否有活动的线程 以及是写入还是阅读线程 引入一个变量m_nActive 如果m_nActive > 则表示当前活动阅读线程的数目 如果m_nActive= 则表示没有任何活动线程 m_nActive < 表示当前有写入线程在活动 注意m_nActive< 时只能取 的值 因为只允许有一个写入线程活动
为了判断当前活动线程拥有的锁的类型 我们采用了线程局部存储技术(请参阅其它参考书籍) 将线程与特殊标志位关联起来
申请阅读锁的函数原型为 public void AcquireReaderLock( int millisecondsTimeout ) 其中的参数为线程等待调度的时间 函数定义如下
public void AcquireReaderLock( int millisecondsTimeout ) { // m_mutext很快可以得到 以便进入临界区 m_mutex WaitOne( ); // 是否有写入线程存在 bool bExistingWriter = ( m_nActive < ); if( bExistingWriter ) { //等待阅读线程数目加 当有锁释放时 根据此数目来调度线程 m_nWaitingReaders++; } else { //当前活动线程加 m_nActive++; } m_mutex ReleaseMutex(); //存储锁标志为Reader System LocalDataStoreSlot slot = Thread GetNamedDataSlot(m_strThreadSlotName); object obj = Thread GetData( slot ); LockFlags flag = LockFlags None; if( obj != null ) flag = (LockFlags)obj ; if( flag == LockFlags None ) { Thread SetData( slot LockFlags Reader ); } else { Thread SetData( slot (LockFlags)((int)flag | (int)LockFlags Reader ) ); } if( bExistingWriter ) { //等待指定的时间 this m_aeReaders WaitOne( millisecondsTimeout true ); } }
它首先进入临界区(用以在多线程环境下保证活动线程数目的操作的正确性)判断当前活动线程的数目 如果有写线程(m_nActive< )存在 则等待指定的时间并且等待的阅读线程数目加 如果当前活动线程是读线程(m_nActive>= ) 则可以让读线程继续运行
申请写入锁的函数原型为 public void AcquireWriterLock( int millisecondsTimeout ) 其中的参数为等待调度的时间 函数定义如下
public void AcquireWriterLock( int millisecondsTimeout ) { // m_mutext很快可以得到 以便进入临界区 m_mutex WaitOne( ); // 是否有活动线程存在 bool bNoActive = m_nActive == ; if( !bNoActive ) { m_nWaitingWriters++; } else { m_nActive ; } m_mutex ReleaseMutex(); //存储线程锁标志 System LocalDataStoreSlot slot = Thread GetNamedDataSlot( myReaderWriterLockDataSlot ); object obj = Thread GetData( slot ); LockFlags flag = LockFlags None; if( obj != null ) flag = (LockFlags)Thread GetData( slot ); if( flag == LockFlags None ) { Thread SetData( slot LockFlags Writer ); } else { Thread SetData( slot (LockFlags)((int)flag | (int)LockFlags Writer ) ); } //如果有活动线程 等待指定的时间 if( !bNoActive ) this m_aeWriters WaitOne( millisecondsTimeout true ); }
它首先进入临界区判断当前活动线程的数目 如果当前有活动线程存在 不管是写线程还是读线程(m_nActive) 线程将等待指定的时间并且等待的写入线程数目加 否则线程拥有写的权限
释放阅读锁的函数原型为 public void ReleaseReaderLock() 函数定义如下
public void ReleaseReaderLock() { System LocalDataStoreSlot slot = Thread GetNamedDataSlot(m_strThreadSlotName ); LockFlags flag = (LockFlags)Thread GetData( slot ); if( flag == LockFlags None ) { return; } bool bReader = true; switch( flag ) { case LockFlags None: break; case LockFlags Writer: bReader = false; break; } if( !bReader ) return; Thread SetData( slot LockFlags None ); m_mutex WaitOne(); AutoResetEvent autoresetevent = null; this m_nActive ; if( this m_nActive == ) { if( this m_nWaitingReaders > ) { m_nActive ++ ; m_nWaitingReaders ; autoresetevent = this m_aeReaders; } else if( this m_nWaitingWriters > ) { m_nWaitingWriters ; m_nActive ; autoresetevent = this m_aeWriters ; } } m_mutex ReleaseMutex(); if( autoresetevent != null ) autoresetevent Set(); }
释放阅读锁时 首先判断当前线程是否拥有阅读锁(通过线程局部存储的标志) 然后判断是否有等待的阅读线程 如果有 先将当前活动线程加 等待阅读线程数目减 然后置事件为有信号 如果没有等待的阅读线程 判断是否有等待的写入线程 如果有则活动线程数目减 等待的写入线程数目减 释放写入锁与释放阅读锁的过程基本一致 可以参看源代码
注意在程序中 释放锁时 只会唤醒一个阅读程序 这是因为使用AutoResetEvent的原历 读者可自行将其改成ManualResetEvent 同时唤醒多个阅读程序 此时应令m_nActive等于整个等待的阅读线程数目
测试
测试程序取自 Net FrameSDK中的一个例子 只是稍做修改 测试程序如下
lishixinzhi/Article/program/net/201311/12614
如何用c++builder 编写多线程
摘 要:本文简单介绍了Windows环境下进行多线程编程的意义,重点讨论了C++Builder环境下开发多线程应用程序这一问题,并通过实现生产者-消费者问题,帮我们更好地理解同步概念及其实现方法。
关键词:多线程;同步;生产者-消费者;C++Builder
线程之可行性
在很多情况下,可能需要为程序创建线程。这里给出其中一些可能性:
(1)如果创建的是一个多文档接口(Multiple Document Interface,MDI)程序,那么为每个窗口分配一个线程就显得十分重要了,例如,对于一个通过多个Modem同时连接到多个主机的MDI通信程序而言,如果每个窗口都有它自己的线程来和一个主机通信,那么整个事情就简化很多。
(2)如果使用的是一台有多个处理器的机器,并希望充分利用所有可能获得的CPU资源,那么就需要将应用程序分解成多个线程。Windows2000中CPU的划分单位为线程。因此,如果程序只包含一个线程,那么,默认环境下该程序只能使用其中一个CPU。但是,如果将此程序划分为多个线程,那么Windows2000就可以在不同的CPU上运行各个线程。
(3)在后台运行的某些任务的同时,要求用户还可以继续使用应用程序进行工作。利用线程很容易实现这点。例如:可以将一些冗长的重算、页面格式化操作、文件的读写等活动都放在单独的线程中,使其在后台运行,而不会对用户造成影响。
同步
撰写多线程程序的一个最具挑战性的问题就是:如何让一个线程和另一个线程合作。这引出了一个非常重要的问题:同步。所谓同步是指进程、线程间相互通信时避免破坏各自数据的能力。Windows环境下的同步问题是由Win32系统的CPU时间片分配方式引起的。虽然在某一时刻,只有一个线程占用CPU(单CPU)时间,但是无法知道在什么时候,在什么地方线程被打断,这样如何保证线程之间不破坏彼此的数据就显得格外重要。同步问题是如此重要,也相当有趣,因而吸引了不少学者对他进行研究,由此产成了一系列经典的进程同步问题,其中较有代表性的是"生产者-消费者问题"、"读者-写者问题""哲学家进餐问题"等。在此,本文简要讨论了C++Builder平台下如何利用多线程编程技术实现"生产者-消费者"问题,帮助我们更好得理解同步概念及其实现方法。
生产者-消费者问题
生产者-消费者问题是一个著名的进程同步问题。它描述的是:有一群生产者进程在生产消息,并将此消息提供给消费者进程去消费。为使生产者进程和消费者进程能并发进行,在他们之间设置了一个具有N个缓冲区的缓冲池,生产者进程可以将它所生产的消息放入一个缓冲区中,消费者进程可以从一个缓冲区中取得一个消息消费。尽管所有的生产者进程和消费者进程都是以异步方式进行的,但他们之间必须保持同步,即不允许消费者进程到一个空的缓冲区中去取消息,也不允许生产者进程向一个已装满消息且尚未被取走消息的缓冲区中投放消息。
C++Builder多线程应用程序编程基础
1、使用C++Builder提供的TThread类
VCL类库提供了用于线程编程的TThread类。在TThread类中封装了Windows中关于线程机制的WindowsAPI。对于大多数的应用程序来说,可在应用程序中使用线程对象来表示执行线程。线程对象通过封装使用线程所需的内容,简化了多线程应用程序的编写。注意,线程对象不允许控制线程堆栈的大小或其安全属性。若需要控制这些,必须使用WindowsAPI的Create Thread()或Begin Thread()函数。
TThread类有以下一些属性和方法:
1) 属性:
·Priority:优先级属性。可以设置线程的优先级。
·Return Value:返回值属性。当线程介绍时返回给其他线程一个数值。
·Suspended:挂起属性。可以判断线程是否被挂起。
·Terminated:结束属性。用来标志是否应该结束线程。
·ThreadID:标识号属性。在整个系统中线程的标识号。使用Windows API函数时该属性非常有用。
2) 方法:
·Do Terminate:产生一个On Terminate事件,但是不结束线程的执行。
·Resume:唤醒一个线程继续执行。
·Suspend:挂起一个线程,要与Resume过程成对使用。
·Synchronize:由主VCL线程调用的一个同步过程。
·Terminate:将Terminate属性设置为True,中止线程的执行。
·Wait For:等待线程的中止并返回Return Value属性的数值。
2、协调线程
在编写线程执行时运行的代码时,必须考虑到可能同步执行的其他线程的行为。特别注意,避免两个线程试图同时使用相同的全局对象或变量。另外,一个线程中的代码会依赖其他线程执行任务的结果。
1) 避免同时访问
为避免在访问全局对象或变量时与其他线程发生冲突,可能需要暂停其他线程的执行,直到该线程代码完成操作。
(1)锁定对象。一些对象内置了锁定功能,以防止其他线程使用该对象的实例。例如,画布对象(TCanvas及其派生类)有一种Lock()函数可以防止其他线程访问画布,直到调用Unlock()函数。显然,这种方法只对部分类有效。
(2)使用重要区段。若对象没有提供内置的锁定功能,可使用重要区段。重要区段像门一样,每次只允许一个线程进入,要使用重要区段,需创建TCriticalSection的全局实例。TCriticalSection有两个函数:Acquire()(阻止其他线程执行该区域)及Release()(取消对其他线程的阻止)。
(3)使用多重读、独占写的同步器。当使用重要区段来保护全局内存时,每次只有一个线程可以使用该内存。这种保护可能会超出了需要,特别是有一个经常读但很少写的对象或变量时更是如此。多个线程同时读相同内存但没有线程写内存是没有危险的。当有一些经常被读,但是很少写的全局变量时,可用TMultiReadExclusiveWriteSynchronizer对象保护它。这个对象和重要区段一样,但它允许多个线程同时读,只要没有线程写即可。每个需要读内存的线程首先要调用Begin Read()函数(确保当前无其他线程写内存),线程完成对保护内存读操作后,要调用End Read()函数。任何线程需要写保护内存必须调用Begin Write()函数(确保当前无其他线程读或写内存),完成对保护内存写操作后,调用End Write()函数。
(4)使用Synchronize函数:Void __fast call Synchronize (TThreadMethod &Method);
其中参数Method为一个不带参数的过程名。在这个不带参数的过程中是一些访问VCL的代码。我们可以在Execute过程中调用Synchronize过程来避免对VCL的并发访问。程序运行期间的具体过程实际上是由Synchronize过程来通知主线程,然后主线程在适当的时机来执行Synchronize过程的参数列表中的那个不带参数的过程。在多个线程的情况下,主线程将Synchronize过程发过来的通知放到消息队列中,然后逐个地响应这些消息。通过这种机制Synchronize实现了线程之间地同步。
2) 等待其他线程
若线程必须等待另一线程完成某项任务,可让线程临时中断执行。然后,要么等待另一线程完全执行结束,要么等待另一线程通知完成了该任务。
(1)等待线程执行结束
要等待另一线程执行结束,使用它地Wait For()函数。Wait For函数直到那个线程终止才返回,终止的方式要么完成了其Execute()函数,要么由于一个异常。
(2)等待任务完成。有时,只需要等待线程完成一些操作而不是等待线程执行结束。为此,可使用一个事件对象。事件对象(TEvent)应具有全局范围以便他们能够为所有线程可见。当一个线程完成一个被其他线程依赖的操作时,调用TEvent::Set Event()函数。Set Event发出一个信号,以便其他线程可以检查并得知操作完成。要关掉信号,则使用Reset Event()函数。
例如,当必须等待若干线程完成其执行而不是单个线程时。因为不知道哪个线程最后完成,也就不能对某个线程使用Wait For()函数。此时,可通过调用Set Event以在线程结束时累加计数值并在最后一个线程结束时发出信号以指示所有线程结束。
多线程应用程序编程实例
下面是一个实现"生产者-消费者问题"的多线程应用实例。在此例中,我们按上面介绍的方法构造了两个TThread的子类TProducerThread(生产者线程)和TCustomerThread(消费者线程),生产和消费的商品仅仅是一个整数。在协调生产和消费的过程中,重要区段(TCriticalSection)和事件(TEvent)得到了应用。生产者通过TEvent类的对象Begin Consume来通知消费者开始消费,而消费者通过TEent类的对象Begin Produce通知生产者开始生产。程序中共有两个生产者,一个消费者。在两个生产者之间,通过TCriticalSection类的对象同步。其运行界面如图1所示。
图1 程序运行效果
主要源程序如下所示:
生产者线程:
Void __fast call TProducerThread:: Execute ()
{
//---- Place thread code here ----
Int i = 0;
Int j;
while(i<100) //每个生产者线程生产100个商品
{
Sleep(1000);//延迟,为清楚得显示执行效果
if(Form1->buffer_size > 0)//缓冲池不空,通知消费者消费
{
Form1->Begin Consumer->Set Event ();
}
Form1->Produce Guard->Acquire ();
i++;
StrResult = IntToStr (i);
J = Form1->buffer_size;
Form1->Product [j] = i;
Form1->buffer_size++;。
摘 要:本文简单介绍了Windows环境下进行多线程编程的意义,重点讨论了C++Builder环境下开发多线程应用程序这一问题,并通过实现生产者-消费者问题,帮我们更好地理解同步概念及其实现方法。
关键词:多线程;同步;生产者-消费者;C++Builder
线程之可行性
在很多情况下,可能需要为程序创建线程。这里给出其中一些可能性:
(1)如果创建的是一个多文档接口(Multiple Document Interface,MDI)程序,那么为每个窗口分配一个线程就显得十分重要了,例如,对于一个通过多个Modem同时连接到多个主机的MDI通信程序而言,如果每个窗口都有它自己的线程来和一个主机通信,那么整个事情就简化很多。
(2)如果使用的是一台有多个处理器的机器,并希望充分利用所有可能获得的CPU资源,那么就需要将应用程序分解成多个线程。Windows2000中CPU的划分单位为线程。因此,如果程序只包含一个线程,那么,默认环境下该程序只能使用其中一个CPU。但是,如果将此程序划分为多个线程,那么Windows2000就可以在不同的CPU上运行各个线程。
(3)在后台运行的某些任务的同时,要求用户还可以继续使用应用程序进行工作。利用线程很容易实现这点。例如:可以将一些冗长的重算、页面格式化操作、文件的读写等活动都放在单独的线程中,使其在后台运行,而不会对用户造成影响。
同步
撰写多线程程序的一个最具挑战性的问题就是:如何让一个线程和另一个线程合作。这引出了一个非常重要的问题:同步。所谓同步是指进程、线程间相互通信时避免破坏各自数据的能力。Windows环境下的同步问题是由Win32系统的CPU时间片分配方式引起的。虽然在某一时刻,只有一个线程占用CPU(单CPU)时间,但是无法知道在什么时候,在什么地方线程被打断,这样如何保证线程之间不破坏彼此的数据就显得格外重要。同步问题是如此重要,也相当有趣,因而吸引了不少学者对他进行研究,由此产成了一系列经典的进程同步问题,其中较有代表性的是"生产者-消费者问题"、"读者-写者问题""哲学家进餐问题"等。在此,本文简要讨论了C++Builder平台下如何利用多线程编程技术实现"生产者-消费者"问题,帮助我们更好得理解同步概念及其实现方法。
生产者-消费者问题
生产者-消费者问题是一个著名的进程同步问题。它描述的是:有一群生产者进程在生产消息,并将此消息提供给消费者进程去消费。为使生产者进程和消费者进程能并发进行,在他们之间设置了一个具有N个缓冲区的缓冲池,生产者进程可以将它所生产的消息放入一个缓冲区中,消费者进程可以从一个缓冲区中取得一个消息消费。尽管所有的生产者进程和消费者进程都是以异步方式进行的,但他们之间必须保持同步,即不允许消费者进程到一个空的缓冲区中去取消息,也不允许生产者进程向一个已装满消息且尚未被取走消息的缓冲区中投放消息。
C++Builder多线程应用程序编程基础
1、使用C++Builder提供的TThread类
VCL类库提供了用于线程编程的TThread类。在TThread类中封装了Windows中关于线程机制的WindowsAPI。对于大多数的应用程序来说,可在应用程序中使用线程对象来表示执行线程。线程对象通过封装使用线程所需的内容,简化了多线程应用程序的编写。注意,线程对象不允许控制线程堆栈的大小或其安全属性。若需要控制这些,必须使用WindowsAPI的Create Thread()或Begin Thread()函数。
TThread类有以下一些属性和方法:
1) 属性:
·Priority:优先级属性。可以设置线程的优先级。
·Return Value:返回值属性。当线程介绍时返回给其他线程一个数值。
·Suspended:挂起属性。可以判断线程是否被挂起。
·Terminated:结束属性。用来标志是否应该结束线程。
·ThreadID:标识号属性。在整个系统中线程的标识号。使用Windows API函数时该属性非常有用。
2) 方法:
·Do Terminate:产生一个On Terminate事件,但是不结束线程的执行。
·Resume:唤醒一个线程继续执行。
·Suspend:挂起一个线程,要与Resume过程成对使用。
·Synchronize:由主VCL线程调用的一个同步过程。
·Terminate:将Terminate属性设置为True,中止线程的执行。
·Wait For:等待线程的中止并返回Return Value属性的数值。
2、协调线程
在编写线程执行时运行的代码时,必须考虑到可能同步执行的其他线程的行为。特别注意,避免两个线程试图同时使用相同的全局对象或变量。另外,一个线程中的代码会依赖其他线程执行任务的结果。
1) 避免同时访问
为避免在访问全局对象或变量时与其他线程发生冲突,可能需要暂停其他线程的执行,直到该线程代码完成操作。
(1)锁定对象。一些对象内置了锁定功能,以防止其他线程使用该对象的实例。例如,画布对象(TCanvas及其派生类)有一种Lock()函数可以防止其他线程访问画布,直到调用Unlock()函数。显然,这种方法只对部分类有效。
(2)使用重要区段。若对象没有提供内置的锁定功能,可使用重要区段。重要区段像门一样,每次只允许一个线程进入,要使用重要区段,需创建TCriticalSection的全局实例。TCriticalSection有两个函数:Acquire()(阻止其他线程执行该区域)及Release()(取消对其他线程的阻止)。
(3)使用多重读、独占写的同步器。当使用重要区段来保护全局内存时,每次只有一个线程可以使用该内存。这种保护可能会超出了需要,特别是有一个经常读但很少写的对象或变量时更是如此。多个线程同时读相同内存但没有线程写内存是没有危险的。当有一些经常被读,但是很少写的全局变量时,可用TMultiReadExclusiveWriteSynchronizer对象保护它。这个对象和重要区段一样,但它允许多个线程同时读,只要没有线程写即可。每个需要读内存的线程首先要调用Begin Read()函数(确保当前无其他线程写内存),线程完成对保护内存读操作后,要调用End Read()函数。任何线程需要写保护内存必须调用Begin Write()函数(确保当前无其他线程读或写内存),完成对保护内存写操作后,调用End Write()函数。
(4)使用Synchronize函数:Void __fast call Synchronize (TThreadMethod &Method);
其中参数Method为一个不带参数的过程名。在这个不带参数的过程中是一些访问VCL的代码。我们可以在Execute过程中调用Synchronize过程来避免对VCL的并发访问。程序运行期间的具体过程实际上是由Synchronize过程来通知主线程,然后主线程在适当的时机来执行Synchronize过程的参数列表中的那个不带参数的过程。在多个线程的情况下,主线程将Synchronize过程发过来的通知放到消息队列中,然后逐个地响应这些消息。通过这种机制Synchronize实现了线程之间地同步。
2) 等待其他线程
若线程必须等待另一线程完成某项任务,可让线程临时中断执行。然后,要么等待另一线程完全执行结束,要么等待另一线程通知完成了该任务。
(1)等待线程执行结束
要等待另一线程执行结束,使用它地Wait For()函数。Wait For函数直到那个线程终止才返回,终止的方式要么完成了其Execute()函数,要么由于一个异常。
(2)等待任务完成。有时,只需要等待线程完成一些操作而不是等待线程执行结束。为此,可使用一个事件对象。事件对象(TEvent)应具有全局范围以便他们能够为所有线程可见。当一个线程完成一个被其他线程依赖的操作时,调用TEvent::Set Event()函数。Set Event发出一个信号,以便其他线程可以检查并得知操作完成。要关掉信号,则使用Reset Event()函数。
例如,当必须等待若干线程完成其执行而不是单个线程时。因为不知道哪个线程最后完成,也就不能对某个线程使用Wait For()函数。此时,可通过调用Set Event以在线程结束时累加计数值并在最后一个线程结束时发出信号以指示所有线程结束。
多线程应用程序编程实例
下面是一个实现"生产者-消费者问题"的多线程应用实例。在此例中,我们按上面介绍的方法构造了两个TThread的子类TProducerThread(生产者线程)和TCustomerThread(消费者线程),生产和消费的商品仅仅是一个整数。在协调生产和消费的过程中,重要区段(TCriticalSection)和事件(TEvent)得到了应用。生产者通过TEvent类的对象Begin Consume来通知消费者开始消费,而消费者通过TEent类的对象Begin Produce通知生产者开始生产。程序中共有两个生产者,一个消费者。在两个生产者之间,通过TCriticalSection类的对象同步。其运行界面如图1所示。
图1 程序运行效果
主要源程序如下所示:
生产者线程:
Void __fast call TProducerThread:: Execute ()
{
//---- Place thread code here ----
Int i = 0;
Int j;
while(i<100) //每个生产者线程生产100个商品
{
Sleep(1000);//延迟,为清楚得显示执行效果
if(Form1->buffer_size > 0)//缓冲池不空,通知消费者消费
{
Form1->Begin Consumer->Set Event ();
}
Form1->Produce Guard->Acquire ();
i++;
StrResult = IntToStr (i);
J = Form1->buffer_size;
Form1->Product [j] = i;
Form1->buffer_size++;
Synchron
转载,仅供参考,祝你愉快,满意请采纳。
如何进行Python多线程编程,一文读懂Python多线程
本文介绍的是Python多线程,想了解Python多线程,得先了解什么是线程。线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。而多线程类似于同时执行多个不同程序,多线程运行有如下优点:1.使用线程可以把占据长时间的程序中的任务放到后台去处理。2.用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度程序的运行速度可能加快3.在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。4.线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。每个线程都有他自己的一组CPU寄存器,称为线程的上下文,该上下文反映了线程上次运行该线程的CPU寄存器的状态。指令指针和堆栈指针寄存器是线程上下文中两个最重要的寄存器,线程总是在进程得到上下文中运行的,这些地址都用于标志拥有线程的进程地址空间中的内存。5.线程可以被抢占(中断)。6.在其他线程正在运行时,线程可以暂时搁置(也称为睡眠) -- 这就是线程的退让。开始学习Python多线程Python中使用线程有两种方式:函数或者用类来包装线程对象。函数式:调用thread模块中的start_new_thread()函数来产生新线程。语法如下:thread.start_new_thread ( function, args[, kwargs] )参数说明:1.function - 线程函数。2.args - 传递给线程函数的参数,他必须是个tuple类型。3.kwargs - 可选参数。附上实例#!/usr/bin/python# -*- coding: UTF-8 -*-import threadimport time# 为线程定义一个函数def print_time( threadName, delay):count = 0while count < 5:time.sleep(delay)count += 1print "%s: %s" % ( threadName, time.ctime(time.time()) )# 创建两个线程try:thread.start_new_thread( print_time, ("Thread-1", 2, ) )thread.start_new_thread( print_time, ("Thread-2", 4, ) )except:print "Error: unable to start thread"while 1:pass执行以上程序输出结果如下:Thread-1: Thu Jan 22 15:42:17 2009Thread-1: Thu Jan 22 15:42:19 2009Thread-2: Thu Jan 22 15:42:19 2009Thread-1: Thu Jan 22 15:42:21 2009Thread-2: Thu Jan 22 15:42:23 2009Thread-1: Thu Jan 22 15:42:23 2009Thread-1: Thu Jan 22 15:42:25 2009Thread-2: Thu Jan 22 15:42:27 2009Thread-2: Thu Jan 22 15:42:31 2009Thread-2: Thu Jan 22 15:42:35 2009线程的结束一般依靠线程函数的自然结束;也可以在线程函数中调用thread.exit(),他抛出SystemExit exception,达到退出线程的目的
Windows多线程编程技术与实例的目录
前言第1章 线程编程基础知识1.1进程与线程1.1.1进程与线程的概念1.1.2进程与线程的比较1.1.3为什么用线程而不用进程1.2直观理解进程和线程1.2.1进程选项卡1.2.2各个进程的详细说明1.2.3性能选项卡1.2.4木马中的线程1.3线程和同步1.3.1互斥体对象1.3.2信号对象1.3.3事件对象1.3.4排斥区对象1.4多线程程序编写起步1.4.1利用Win32 API的线程编程技术1.4.2利用MFC进行多线程编程1.5多线程的性能1.5.1计算问题的类型1.5.2多线程设计的目标1.5.3基于I/O的任务1.5.4基于CPU的任务1.5.5 Windows 95和Windows NT之间的比较1.5.6什么时候采用多线程小结习题第2章 进入多线程世界2.1入门实例——多线程的简单应用2.1.1程序运行一览2.1.2编译和运行……第3章 多进程编程第4章 用Win32 API进行多线程程序设计第5章 使用MFC进行多线程程序设计第6章 多线程技术在网络编程中的应用第7章 多线程在图形图象中的应用第8章 多线程在具体项目中的应用
本人初学java想找些编程例子,巩固前面所学,最好关于面向对象三大思想方面和多线程、IO等。不要选择题。
【试题1】任务一:求阶乘
l 接受一个输入,输出这个数的阶乘
l 用长整型计算
任务二:输出阶梯形式的9*9口诀表,如图1.1所示。
1*1=1
1*2=2 2*2=4
1*3=3 2*3=6 3*3=9
1*4=4 2*4=8 3*4=12 4*4=16
1*5=5 2*5=10 3*5=15 4*5=20 5*5=25
1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36
1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49
1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64
1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81
图1.1 阶梯形式的9*9口诀表
要求:使用循环结构语句实现。
任务三:编程实现判断一个整数是否为“水仙花数”。所谓“水仙花数”是指一个三位的整数,其各位数字立方和等于该数本身。例如:153是一个“水仙花数”,因为153=13+53+33。
要求:用带有一个输入参数的方法或函数实现,返回值类型为布尔类型。
【试题2】
任务一:已知某个班有M个学生,学习N门课程,已知所有学生的各科成绩,编程:分别求每个学生的平均成绩,并输出。
要求:
l 定义一个二维数组,用于存放M个学生的N门成绩。定义一个一维数组,用于存放每个学生的平均成绩。
l 做二重循环,将每个学生的成绩输入到该二维数组中。
l 做二重循环,对已经存在于二维数组的中的值进行平均分计算,将结果保存到一个一维数组中。
l 做循环输出该一维数组(即平均分)的值。
任务二:编写一个程序找出100~1000之间的所有姐妹素数。
注:姐妹素数是指相邻两个奇数均为素数。
要求:使用循环结构语句实现。
任务三:求n以内(不包括n)同时能被3和7整除的所有自然数之和的平方根s,并作为函数值返回,最后结果s输出到文件out.txt中。例如若n为1000时,函数值应为:s=153.909064。
【试题3】
任务一:求2!+3!+4!+5!
任务二:编写一个程序,对用户输入的任意一组数字字符如{3,1,4,7,2,1,1,2,2},输出其中出现次数最多的字符,并显示其出现次数。如果有多个字符出现次数均为最大且相等,则输出最先出现的那个字符和它出现的次数。例如,上面输入的字符集合中,“1”和“2”都出现了3次,均为最大出现次数,因为“1”先出现,则输出字符“1”和它出现的次数3次。
任务三:中华人民共和国2011年新的个人所得税草案规定,个税的起征点为3000元,分成7级,税率情况见表1.3, 从键盘上输入月工资,计算应交纳的个人所得税。
表1.3税率情况表
级数
全月应纳税所得额
税率 (%)
1
不超过1500元的(即3000-4500之间)
5
2
超过1500元至4500元的部分
10
3
超过4500元至9000元的部分
20
4
超过9000元至35000元的部分
25
5
超过35000元至55000元的部分
30
6
超过55000元至80000元的部分
35
7
超过80000元的部分
45
注意:超出部分按所在税的级数计算,如:一个人的月收入为6000,应交个人所得税为:1500*0.05 +((6000-3000)-1500)*0.1=225
请在键盘上输入一个人的月收入,编程实现该公民所要交的税。
例如:输入“4000”,则输出“你要交的税为:50”。
【试题4】
任务一:请编写函数fun,其功能是:将两个两位数的正整数a、b合并形成一个整数放在c中。合并的方式是:将a数的十位和个位数依次放在c数个位和十位上,b数的十位和个位数依次放在c数的百位和千位上。例如,当a=16,b=35,调用该函数后,c=5361。
任务二:孙悟空在大闹蟠桃园的时候,第一天吃掉了所有桃子总数一半多一个,第二天又将剩下的桃子吃掉一半多一个,以后每天吃掉前一天剩下的一半多一个,到第n天准备吃的时候只剩下一个桃子。这下可把神仙们心疼坏了,请帮忙计算一下,第一天开始吃的时候桃园一共有多少个桃子。
要求:用循环语句实现,从键盘随机输入参数n
任务三:输入一个5位正整数,输出它是不是回文数。回文数是这样一种数,它的逆序数和它本身相等。例如,12321的逆序数是12321,和它本身相等,所以它是回文数。又例如25128的逆序数是82152,所以它不是回文数。
要求:使用分支或循环结构语句实现。
【试题5】
任务一:判断一个年份N闰年;
输入:年份N
任务二:一个球从100m高度自由落下,每次落地后反弹回原高度的一半,再落下,再反弹。求它在第十次落地时,共经过多少米?第十次反弹多高?
任务三:Redraiment的老家住在工业区,日耗电量非常大。是政府的眼中钉肉中刺,但又没办法,这里头住的可都是纳税大户呀。
今年7月,又传来了不幸的消息,政府要在7、8月对该区进行拉闸限电。但迫于压力,限电制度规则不会太抠门,政府决定从7月1日停电,然后隔一天到7月3日再停电,再隔两天到7月6日停电,依次下去,每次都比上一次晚一天。
Redraiment可是软件专业的学生,怎么离得开计算机。如果停电,就“英雄无用武之地”了。所以他开始盘算起自己回家的日子了,他想知道自己到家后到底要经历多少天倒霉的停电。你能帮他算一算吗?
要求:从键盘输入放假日期,开学日期,日期限定在7、8月份,且开学日期大于放假日期。
提示:可以用数组标记停电的日期
【试题6】
任务一:定义一个电脑类,包含电脑的品牌,价格,型号。
任务二:某班同学上体育课,从1开始报数,共38人,老师要求按1,2,3重复报数,报数为1的同学往前走一步,而报数为2的同学往后退一步,试分别将往前走一步和往后退一步的同学的序号打印出来。
要求:用循环语句实现
任务三:一个人很倒霉,不小心打碎了一位妇女的一篮子鸡蛋。为了赔偿便询问篮子里有多少鸡蛋。那妇女说,她也不清楚,只记得每次拿两个则剩一个,每次拿3个则剩2个,每次拿5个则剩4个,若每个鸡蛋1元,请你帮忙编程,计算最少应赔多少钱?
要求:用循环语句实现,直接打印出结果不给分
【试题7】
任务一:从键盘接收一个整数N,统计出1~N之间能被7整除的整数的个数,以及这些能被7整除的数的和。
屏幕提示样例:
请输入一个整数:20
1~20之间能被7整除的数的个数:2
1~20之间能被7整除的所有数之和:21
任务二:从键盘输入一个整数N,打印出有N*2-1行的菱形。
例如输入整数4,则屏幕输出如下菱形。
要求:使用循环结构语句实现。
任务三:编程实现判断一个整数是否为素数。所谓素整是一个大于1的正整数,除了1和它本身,该数不能被其它的正整数整除。
要求:用带有一个输入参数的方法或函数实现,返回值类型为布尔类型。
【试题8】
任务一:设计一个学生类,包括学号,姓名,班次,系别,联系电话,并包含一个构造方法。
要求:构造方法必须有5个参数,能对学生类中的属性进行赋初值。
任务二:使用冒泡排序法对数组中的整数按升序进行排序,如下所示:
原始数组:a[]={1,9,3,7,4,2,5,0,6,8}
排序后: a[]={0,1,2,3,4,5,6,7,8,9}
要求:使用循环结构语句实现。
任务三:编程实现以下要求。n个人围坐成一卷,从第一个人开始计数,数到m,第m个人出列,接下来继续计数,直到所有人都出列。例如:共有5个人,数到3出列,则顺序为3,1,5,2,4。
要求:用带有两个输入参数(一个总人数n,一个为计数m)的方法或函数实现,返回值类型为数组。
【试题9】
任务一:输入一行字符,输出其中的数字的个数。例如输入“fwEt2f44F2k8”,输出结果为5。
任务二:用循环打印杨辉三角到N层
输入:自然数N
任务三:删去一维数组中相同的数
输入 2 3 3 3 4 5 2 9 10 9 3 2 4
输出2 3 4 5 9 10
【试题10】
任务一:定义一个矩形类,包括长,宽两个属性,计算周长方法和计算面积方法。
任务二:编写程序实现:输入一个整数,判断它能否被3、5、7整除,并输出以下信息之一:
能同时被3、5、7整除
能同时被3、5整除
能同时被3、7整除
能同时被5、7整除
只能被3、5、7中的一个整除
不能被3、5、7任一个整除
要求:使用分支结构语句实现。
任务三:寻找最大数经常在计算机应用程序中使用。例如:确定销售竞赛优胜者的程序要输入每个销售员的销售量,销量最大的员工为销售竞赛的优胜者,写一个程序:从键盘输入10个数,打印出其中最大的数。
要求:程序应正确使用如下两个变量:number:当前输入程序的数,largest:到目前为止的最大数。
给你30个题 慢慢做吧
Windows多线程编程技术与实例的内容简介
本书通过众多实例介绍了如何实现Windows下的多线程编程,既重点介绍了Win32API下的多线程编程和MFC下的多线程编程,又介绍了多线程机制在网络编程、图形编程和数据库中的应用。本书每一章都从简单的多线程实例出发逐渐深入,紧紧围绕应用程序实例,向读者展示了利用多线程技术来编写高效、友好的Windows应用程序的方法,并对常用的Win32线程函数进行了深入详细的说明。本书共分8章,第l章介绍了多线程编程的基础知识;第2~5章通过实例阐明Win32下多线程的几种不同实现形式及多进程的实现机制,这是本书介绍的重点内容,也是读者学习后面几章内容所必须掌握的基础知识;第6~8章介绍了多线程技术在网络、图形处理和数据库中的应用。本书语言通俗易懂,内容丰富翔实,突出了以实例为中心的特点,既适合具有一定c++和VC编程基础的高校相关专业学生选作多线程编程的学习用书,也适用于具有一定实际编程经验的中高级开发人员作为学习多线程编程思想的自学用书。
如何用servlet实现多线程编程
Servlet的多线程和线程安全
线程安全
首先说明一下对线程安全的讨论,哪种情况我们可以称作线程安全?
网上对线程安全有很多描述,我比较喜欢《Java并发编程实战》给出的定义,“当多个线程访问某个类时,不管运行时环境采用何种调度方式,或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的”。
Servlet的调用过程和生命周期
Servlet的生命周期
Servlet是运行在Servlet容器中的,常用的tomcat、jboss、weblogic都是Servlet容器,其生命周期是由容器来管理。Servlet的生命周期通过java.servlet.Servlet接口中的init()、service()、和destroy()方法表示。Servlet的生命周期有四个阶段:加载并实例化、初始化、请求处理、销毁。
加载并实例化
Servlet容器负责加载和实例化Servelt。当Servlet容器启动时,或者在容器检测到需要这个Servlet来响应第一个请求时,创建Servlet实例。当Servlet容器启动后,Servlet通过类加载器来加载Servlet类,加载完成后再new一个Servlet对象来完成实例化。
初始化
在Servlet实例化之后,容器将调用init()方法,并传递实现ServletConfig接口的对象。在init()方法中,Servlet可以部署描述符中读取配置参数,或者执行任何其他一次性活动。在Servlet的整个生命周期类,init()方法只被调用一次。
请求处理
当Servlet初始化后,容器就可以准备处理客户机请求了。当容器收到对这一Servlet的请求,就调用Servlet的service()方法,并把请求和响应对象作为参数传递。当并行的请求到来时,多个service()方法能够同时运行在独立的线程中。通过分析ServletRequest或者HttpServletRequest对象,service()方法处理用户的请求,并调用ServletResponse或者HttpServletResponse对象来响应。
销毁
一旦Servlet容器检测到一个Servlet要被卸载,这可能是因为要回收资源或者因为它正在被关闭,容器会在所有Servlet的service()线程之后,调用Servlet的destroy()方法。然后,Servlet就可以进行无用存储单元收集清理。这样Servlet对象就被销毁了。这四个阶段共同决定了Servlet的生命周期。
Servlet的调用过程
1.客户端通过发送请求给Tomcat,Tomcat发送客户端的请求页面给客户端。
2.用户对请求页面进行相关操作后将页面提交给Tomcat,Tomcat将其封装成一个HttpRequest对象,然后对请求进行处理,。
3.Tomcat截获请求,根据action属性值查询xml文件中对应的servlet-name,再根据servlet-name查询到对应的java类(如果是第一次,Tomcat则会将servlet编译成java类文件,所以如果servlet有很多的话第一次运行的时候程序会比较慢)。
4.Tomcat实例化查询到的java类,注意该类只实例化一次。
5.调用java类对象的service()方法(如果不对service()方法进行重写则根据提交的方式来决定执行doPost()方法还是doGet()方法)。
6.通过request对象取得客户端传过来的数据,对数据进行处理后通过response对象将处理结果写回客户端。
Servlet是线程安全的吗?
从上面Servlet的调用过程可以看出,当客户端第一次请求Servlet的时候,tomcat会根据web.xml配置文件实例化servlet,
当又有一个客户端访问该servlet的时候,不会再实例化该servlet,也就是多个线程在使用这个实例。
JSP/Servlet容器默认是采用单实例多线程(这是造成线程安全的主因)方式处理多个请求的,这种默认以多线程方式执行的设计可大大降低对系统的资源需求,提高系统的并发量及响应时间。
Servlet本身是无状态的,一个无状态的Servlet是绝对线程安全的,无状态对象设计也是解决线程安全问题的一种有效手段。
所以,servlet是否线程安全是由它的实现来决定的,如果它内部的属性或方法会被多个线程改变,它就是线程不安全的,反之,就是线程安全的。
下面这个示例来自《Java并发编程实战》,在竞态条件下存在线程不安全。
深入解析Python中的线程同步方法
深入解析Python中的线程同步方法
同步访问共享资源
在使用线程的时候,一个很重要的问题是要避免多个线程对同一变量或其它资源的访问冲突。一旦你稍不留神,重叠访问、在多个线程中修改(共享资源)等这些操作会导致各种各样的问题;更严重的是,这些问题一般只会在比较极端(比如高并发、生产服务器、甚至在性能更好的硬件设备上)的情况下才会出现。
比如有这样一个情况:需要追踪对一事件处理的次数
counter = 0
def process_item(item):
global counter
... do something with item ...
counter += 1
如果你在多个线程中同时调用这个函数,你会发现counter的值不是那么准确。在大多数情况下它是对的,但有时它会比实际的少几个。
出现这种情况的原因是,计数增加操作实际上分三步执行:
解释器获取counter的当前值计算新值将计算的新值回写counter变量
考虑一下这种情况:在当前线程获取到counter值后,另一个线程抢占到了CPU,然后同样也获取到了counter值,并进一步将counter值重新计算并完成回写;之后时间片重新轮到当前线程(这里仅作标识区分,并非实际当前),此时当前线程获取到counter值还是原来的,完成后续两步操作后counter的值实际只加上1。
另一种常见情况是访问不完整或不一致状态。这类情况主要发生在一个线程正在初始化或更新数据时,另一个进程却尝试读取正在更改的数据。
原子操作
实现对共享变量或其它资源的同步访问最简单的方法是依靠解释器的原子操作。原子操作是在一步完成执行的操作,在这一步中其它线程无法获得该共享资源。
通常情况下,这种同步方法只对那些只由单个核心数据类型组成的共享资源有效,譬如,字符串变量、数字、列表或者字典等。下面是几个线程安全的操作:
读或者替换一个实例属性读或者替换一个全局变量从列表中获取一项元素原位修改一个列表(例如:使用append增加一个列表项)从字典中获取一项元素原位修改一个字典(例如:增加一个字典项、调用clear方法)
注意,上面提到过,对一个变量或者属性进行读操作,然后修改它,最终将其回写不是线程安全的。因为另外一个线程会在这个线程读完却没有修改或回写完成之前更改这个共享变量/属性。
锁
锁是Python的threading模块提供的最基本的同步机制。在任一时刻,一个锁对象可能被一个线程获取,或者不被任何线程获取。如果一个线程尝试去获取一个已经被另一个线程获取到的锁对象,那么这个想要获取锁对象的线程只能暂时终止执行直到锁对象被另一个线程释放掉。
锁通常被用来实现对共享资源的同步访问。为每一个共享资源创建一个Lock对象,当你需要访问该资源时,调用acquire方法来获取锁对象(如果其它线程已经获得了该锁,则当前线程需等待其被释放),待资源访问完后,再调用release方法释放锁:
lock = Lock()
lock.acquire() #: will block if lock is already held
... access shared resource
lock.release()
注意,即使在访问共享资源的过程中出错了也应该释放锁,可以用try-finally来达到这一目的:
lock.acquire()
try:
... access shared resource
finally:
lock.release() #: release lock, no matter what
在Python 2.5及以后的版本中,你可以使用with语句。在使用锁的时候,with语句会在进入语句块之前自动的获取到该锁对象,然后在语句块执行完成后自动释放掉锁:
from __future__ import with_statement #: 2.5 only
with lock:
... access shared resource
acquire方法带一个可选的等待标识,它可用于设定当有其它线程占有锁时是否阻塞。如果你将其值设为False,那么acquire方法将不再阻塞,只是如果该锁被占有时它会返回False:
if not lock.acquire(False):
... 锁资源失败
else:
try:
... access shared resource
finally:
lock.release()
你可以使用locked方法来检查一个锁对象是否已被获取,注意不能用该方法来判断调用acquire方法时是否会阻塞,因为在locked方法调用完成到下一条语句(比如acquire)执行之间该锁有可能被其它线程占有。
if not lock.locked():
#: 其它线程可能在下一条语句执行之前占有了该锁
lock.acquire() #: 可能会阻塞
简单锁的缺点
标准的锁对象并不关心当前是哪个线程占有了该锁;如果该锁已经被占有了,那么任何其它尝试获取该锁的线程都会被阻塞,即使是占有锁的这个线程。考虑一下下面这个例子:
lock = threading.Lock()
def get_first_part():
lock.acquire()
try:
... 从共享对象中获取第一部分数据
finally:
lock.release()
return data
def get_second_part():
lock.acquire()
try:
... 从共享对象中获取第二部分数据
finally:
lock.release()
return data
示例中,我们有一个共享资源,有两个分别取这个共享资源第一部分和第二部分的函数。两个访问函数都使用了锁来确保在获取数据时没有其它线程修改对应的共享数据。
现在,如果我们想添加第三个函数来获取两个部分的数据,我们将会陷入泥潭。一个简单的方法是依次调用这两个函数,然后返回结合的结果:
def get_both_parts():
first = get_first_part()
seconde = get_second_part()
return first, second
这里的问题是,如有某个线程在两个函数调用之间修改了共享资源,那么我们最终会得到不一致的数据。最明显的解决方法是在这个函数中也使用lock:
def get_both_parts():
lock.acquire()
try:
first = get_first_part()
seconde = get_second_part()
finally:
lock.release()
return first, second
然而,这是不可行的。里面的两个访问函数将会阻塞,因为外层语句已经占有了该锁。为了解决这个问题,你可以通过使用标记在访问函数中让外层语句释放锁,但这样容易失去控制并导致出错。幸运的是,threading模块包含了一个更加实用的锁实现:re-entrant锁。
Re-Entrant Locks (RLock)
RLock类是简单锁的另一个版本,它的特点在于,同一个锁对象只有在被其它的线程占有时尝试获取才会发生阻塞;而简单锁在同一个线程中同时只能被占有一次。如果当前线程已经占有了某个RLock锁对象,那么当前线程仍能再次获取到该RLock锁对象。
lock = threading.Lock()
lock.acquire()
lock.acquire() #: 这里将会阻塞
lock = threading.RLock()
lock.acquire()
lock.acquire() #: 这里不会发生阻塞
RLock的主要作用是解决嵌套访问共享资源的问题,就像前面描述的示例。要想解决前面示例中的问题,我们只需要将Lock换为RLock对象,这样嵌套调用也会OK.
lock = threading.RLock()
def get_first_part():
... see above
def get_second_part():
... see above
def get_both_parts():
... see above
这样既可以单独访问两部分数据也可以一次访问两部分数据而不会被锁阻塞或者获得不一致的数据。
注意RLock会追踪递归层级,因此记得在acquire后进行release操作。
Semaphores
信号量是一个更高级的锁机制。信号量内部有一个计数器而不像锁对象内部有锁标识,而且只有当占用信号量的线程数超过信号量时线程才阻塞。这允许了多个线程可以同时访问相同的代码区。
semaphore = threading.BoundedSemaphore()
semaphore.acquire() #: counter减小
... 访问共享资源
semaphore.release() #: counter增大
当信号量被获取的时候,计数器减小;当信号量被释放的时候,计数器增大。当获取信号量的时候,如果计数器值为0,则该进程将阻塞。当某一信号量被释放,counter值增加为1时,被阻塞的线程(如果有的话)中会有一个得以继续运行。
信号量通常被用来限制对容量有限的资源的访问,比如一个网络连接或者数据库服务器。在这类场景中,只需要将计数器初始化为最大值,信号量的实现将为你完成剩下的事情。
max_connections = 10
semaphore = threading.BoundedSemaphore(max_connections)
如果你不传任何初始化参数,计数器的值会被初始化为1.
Python的threading模块提供了两种信号量实现。Semaphore类提供了一个无限大小的信号量,你可以调用release任意次来增大计数器的值。为了避免错误出现,最好使用BoundedSemaphore类,这样当你调用release的次数大于acquire次数时程序会出错提醒。
线程同步
锁可以用在线程间的同步上。threading模块包含了一些用于线程间同步的类。
Events
一个事件是一个简单的同步对象,事件表示为一个内部标识(internal flag),线程等待这个标识被其它线程设定,或者自己设定、清除这个标识。
event = threading.Event()
#: 一个客户端线程等待flag被设定
event.wait()
#: 服务端线程设置或者清除flag
event.set()
event.clear()
一旦标识被设定,wait方法就不做任何处理(不会阻塞),当标识被清除时,wait将被阻塞直至其被重新设定。任意数量的线程可能会等待同一个事件。
Conditions
条件是事件对象的高级版本。条件表现为程序中的某种状态改变,线程可以等待给定条件或者条件发生的信号。
下面是一个简单的生产者/消费者实例。首先你需要创建一个条件对象:
#: 表示一个资源的附属项
condition = threading.Condition()
生产者线程在通知消费者线程有新生成资源之前需要获得条件:
#: 生产者线程
... 生产资源项
condition.acquire()
... 将资源项添加到资源中
condition.notify() #: 发出有可用资源的信号
condition.release()
消费者必须获取条件(以及相关联的锁),然后尝试从资源中获取资源项:
#: 消费者线程
condition.acquire()
while True:
...从资源中获取资源项
if item:
break
condition.wait() #: 休眠,直至有新的资源
condition.release()
... 处理资源
wait方法释放了锁,然后将当前线程阻塞,直到有其它线程调用了同一条件对象的notify或者notifyAll方法,然后又重新拿到锁。如果同时有多个线程在等待,那么notify方法只会唤醒其中的一个线程,而notifyAll则会唤醒全部线程。
为了避免在wait方法处阻塞,你可以传入一个超时参数,一个以秒为单位的浮点数。如果设置了超时参数,wait将会在指定时间返回,即使notify没被调用。一旦使用了超时,你必须检查资源来确定发生了什么。
注意,条件对象关联着一个锁,你必须在访问条件之前获取这个锁;同样的,你必须在完成对条件的访问时释放这个锁。在生产代码中,你应该使用try-finally或者with.
可以通过将锁对象作为条件构造函数的参数来让条件关联一个已经存在的锁,这可以实现多个条件公用一个资源:
lock = threading.RLock()
condition_1 = threading.Condition(lock)
condition_2 = threading.Condition(lock)
互斥锁同步
我们先来看一个例子:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import time, threading
# 假定这是你的银行存款:
balance = 0
muxlock = threading.Lock()
def change_it(n):
# 先存后取,结果应该为0:
global balance
balance = balance + n
balance = balance - n
def run_thread(n):
# 循环次数一旦多起来,最后的数字就变成非0
for i in range(100000):
change_it(n)
t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t3 = threading.Thread(target=run_thread, args=(9,))
t1.start()
t2.start()
t3.start()
t1.join()
t2.join()
t3.join()
print balance
结果 :
[/data/web/test_python]$ python multhread_threading.py
0
[/data/web/test_python]$ python multhread_threading.py
61
[/data/web/test_python]$ python multhread_threading.py
0
[/data/web/test_python]$ python multhread_threading.py
24
上面的例子引出了多线程编程的最常见问题:数据共享。当多个线程都修改某一个共享数据的时候,需要进行同步控制。
线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。互斥锁为资源引入一个状态:锁定/非锁定。某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。
threading模块中定义了Lock类,可以方便的处理锁定:
#创建锁mutex = threading.Lock()
#锁定mutex.acquire([timeout])
#释放mutex.release()
其中,锁定方法acquire可以有一个超时时间的可选参数timeout。如果设定了timeout,则在超时后通过返回值可以判断是否得到了锁,从而可以进行一些其他的处理。
使用互斥锁实现上面的例子的代码如下:
balance = 0
muxlock = threading.Lock()
def change_it(n):
# 获取锁,确保只有一个线程操作这个数
muxlock.acquire()
global balance
balance = balance + n
balance = balance - n
# 释放锁,给其他被阻塞的线程继续操作
muxlock.release()
def run_thread(n):
for i in range(10000):
change_it(n)
加锁后的结果,就能确保数据正确:
[/data/web/test_python]$ python multhread_threading.py
0
[/data/web/test_python]$ python multhread_threading.py
0
[/data/web/test_python]$ python multhread_threading.py
0
[/data/web/test_python]$ python multhread_threading.py
0
Java多线程锁如何进行数据同步共享
Java多线程锁是为了解决数据同步中的数据安全问题 下面我们就来详细的学习下有关于Java多线程锁的相关问题 只有不断的学习才能不断的提高自身的相关技术
大多数应用程序要求线程互相通信来同步它们的动作 在Java程序中最简单实现同步的方法就是上Java多线程锁 为了防止同时访问共享资源 线程在使用资源的前后可以给该资源上锁和开锁 假想给复印机上锁 任一时刻只有一个职员拥有钥匙 若没有钥匙就不能使用复印机
给共享变量上Java多线程锁就使得Java线程能够快速方便地通信和同步 某个线程若给一个对象上了锁 就可以知道没有其他线程能够访问该对象 即使在抢占式模型中 其他线程也不能够访问此对象 直到上锁的线程被唤醒 完成工作并开锁 那些试图访问一个上锁对象的线程通常会进入睡眠状态 直到上锁的线程开锁 一旦锁被打开 这些睡眠进程就会被唤醒并移到准备就绪队列中
在Java编程中 所有的对象都有锁 线程可以使用synchronized关键字来获得锁 在任一时刻对于给定的类的实例 方法或同步的代码块只能被一个线程执行 这是因为代码在执行之前要求获得对象的Java多线程锁 继续我们关于复印机的比喻 为了避免复印冲突 我们可以简单地对复印资源实行同步 如同下列的代码例子 任一时刻只允许一位职员使用复印资源 通过使用方法(在 Copier 对象中)来修改复印机状态 这个方法就是同步方法 只有一个线程能够执行一个Copier对象中同步代码 因此那些需要使用Copier对象的职员就必须排队等候
class CopyMachine {
public synchronized void makeCopies(Document d int nCopies) {
//only one thread executes this at a time
}
public void loadPaper() {
//multiple threads could access this at once!
synchronized(this) {
//only one thread accesses this at a time
//feel free to use shared resources overwrite members etc
Fine grain Java多线程锁
在对象级使用锁通常是一种比较粗糙的方法 为什么要将整个对象都上锁 而不允许其他线程短暂地使用对象中其他同步方法来访问共享资源?如果一个对象拥有多个资源 就不需要只为了让一个线程使用其中一部分资源 就将所有线程都锁在外面 由于每个对象都有Java多线程锁 可以如下所示使用虚拟对象来上锁
class FineGrainLock {
MyMemberClass x y;
Object xlock = new Object() ylock = new Object();
public void foo() {
synchronized(xlock) {
//access x here
}
//do something here but don t use shared resources
synchronized(ylock) {
//access y here
}
}
public void bar() {
synchronized(this) {
//access both x and y here
}
//do something here but don t use shared resources
}
}
lishixinzhi/Article/program/Java/gj/201311/27267