大多数服务对象需要能够通过启动单独的硬件读取器线程来异步响应硬件事件。 服务对象是服务点应用程序与硬件之间的链接。 因此,服务对象必须从关联的硬件读取数据,同时仍可供应用程序使用。
本部分演示实现多线程服务对象所需代码的一种方法。
要求
若要编译此代码,应用程序必须包含对 System.Threading 命名空间的引用。
下面的示例实现了一个线程帮助程序类,该类可由服务对象实现使用,但不自行编译或运行。
演示
此示例演示服务对象如何使用线程支持异步监视硬件事件。 示例代码实现线程帮助程序类,该类可用于向服务对象添加基本线程支持。
若要使用本节中提供的线程帮助程序类,需要创建一个派生自 ServiceObjectThreadHelper 的类(包含在下面的代码中),并实现以下方法:
ServiceObjectThreadOpen 初始化完成后,将从线程帮助程序类的 OpenThread 方法调用此方法。 在此处实现任何特定于硬件的初始化代码。 此方法为 virtual 方法。 将返回默认实现。
ServiceObjectThreadClose 当线程帮助程序对象终止其线程或调用 Dispose 方法时,将调用此方法,并且应该用于释放任何非托管句柄或与设备相关的其他资源。 此方法为 virtual 方法。 将返回默认实现。
ServiceObjectProcedure 完成所有初始化并成功启动线程后,将调用此方法。 此方法是抽象的,必须在派生自线程帮助程序类的类中实现。 ServiceObjectProcedure 方法采用单个参数,即 ManualEvent 句柄。 设置此句柄后,必须退出线程过程。 这是通过在 while 循环中调用 ManualEvent.WaitOne 来完成的。 例如:
while (true) { // Wait for a hardware event or the thread stop event. // Test to see if the thread terminated event is set and // exit the thread if so. if (ThreadStopEvent.WaitOne(0, false)) { break; } // The thread is not terminating, so it must be a // a hardware event. }
示例
using System;
using System.Threading;
using Microsoft.PointOfService;
namespace Samples.ServiceObjects.Advanced
{
// The following code implements a thread helper class.
// This class may be used by other Point Of Service
// samples which may require a separate thread for monitoring
// hardware.
public abstract class ServiceObjectThreadHelper : IDisposable
{
// The thread object which will wait for data from the POS
// device.
private Thread ReadThread;
// These events signal that the thread is starting or stopping.
private AutoResetEvent ThreadTerminating;
private AutoResetEvent ThreadStarted;
// Keeps track of whether or not a thread should
// be running.
bool ThreadWasStarted;
public ServiceObjectThreadHelper()
{
// Create events to signal the reader thread.
ThreadTerminating = new AutoResetEvent(false);
ThreadStarted = new AutoResetEvent(false);
ThreadWasStarted = false;
// You need to handle the ApplicationExit event so
// that you can properly clean up the thread.
System.Windows.Forms.Application.ApplicationExit +=
new EventHandler(Application_ApplicationExit);
}
~ServiceObjectThreadHelper()
{
Dispose(true);
}
public virtual void ServiceObjectThreadOpen()
{
return;
}
public virtual void ServiceObjectThreadClose()
{
return;
}
// This is called when the thread starts successfully and
// will be run on the new thread.
public abstract void ServiceObjectThreadProcedure(
AutoResetEvent ThreadStopEvent);
private bool IsDisposed = false;
protected virtual void Dispose(bool disposing)
{
if (!IsDisposed)
{
try
{
if (disposing == true)
{
CloseThread();
}
}
finally
{
IsDisposed = true;
}
}
}
public void Dispose()
{
Dispose(true);
// This object has been disposed of, so no need for
// the GC to call the finalization code again.
GC.SuppressFinalize(this);
}
public void OpenThread()
{
try
{
// Check to see if this object is still valid.
if (IsDisposed)
{
// Throw system exception to indicate that
// the object has already been disposed.
throw new ObjectDisposedException(
"ServiceObjectSampleThread");
}
// In case the application has called OpenThread
// before calling CloseThread, stop any previously
// started thread.
SignalThreadClose();
ServiceObjectThreadOpen();
// Reset event used to signal the thread to quit.
ThreadTerminating.Reset();
// Reset the event that used by the thread to signal
// that it has started.
ThreadStarted.Reset();
// Create the thread object and give it a name. The
// method used here, ThreadMethod, is a wrapper around
// the actual thread procedure, which will be run in
// the threading object provided by the Service
// Object.
ReadThread = new Thread(
new ThreadStart(ThreadMethod));
// Set the thread background mode.
ReadThread.IsBackground = false;
// Finally, attempt to start the thread.
ReadThread.Start();
// Wait for the thread to start, or until the time-out
// is reached.
if (!ThreadStarted.WaitOne(3000, false))
{
// If the time-out was reached before the event
// was set, then throw an exception.
throw new PosControlException(
"Unable to open the device for reading",
ErrorCode.Failure);
}
// The thread has started successfully.
ThreadWasStarted = true;
}
catch (Exception e)
{
// If an error occurred, be sure the new thread is
// stopped.
CloseThread();
// Re-throw to let the application handle the
// failure.
throw;
}
}
private void SignalThreadClose()
{
if(ThreadTerminating != null && ThreadWasStarted)
{
// Tell the thread to terminate.
ThreadTerminating.Set();
// Give the thread a few seconds to end.
ThreadStarted.WaitOne(10000, false);
// Mark the thread as being terminated.
ThreadWasStarted = false;
}
}
public void CloseThread()
{
// Signal the thread that it should stop.
SignalThreadClose();
// Call back into the SO for any cleanup.
ServiceObjectThreadClose();
}
private void Application_ApplicationExit(
object sender,
EventArgs e)
{
SignalThreadClose();
}
// This is the method run on the new thread. First it signals
// the caller indicating that the thread has started
// correctly. Next, it calls the service object's thread
// method which will loop waiting for data or a signal
// to close.
private void ThreadMethod()
{
try
{
// Set the event to indicate that the thread has
// started successfully.
ThreadStarted.Set();
// Call into the thread procedure defined by the
// Service Object.
ServiceObjectThreadProcedure(ThreadTerminating);
// Signal that the thread procedure is exiting.
ThreadStarted.Set();
}
catch (Exception e)
{
Logger.Info("ServiceObjectThreadHelper",
"ThreadMethod Exception = " + e.ToString());
throw;
}
}
}
}