C# - 在WinForm的后台检查设备状态

时间:2013-05-30 19:21:22

标签: c# .net multithreading winforms background-process

我正在C#中构建一个小型WinForms应用程序,该应用程序将与条形码扫描器连接,并允许我通过扫描条形码,对它们执行操作然后发送信息来签入/签出标有条形码的项目回到中央服务器。我想继续检查两件事:

  1. 条形码扫描器是否连接到设备?
  2. 有网络连接吗?如果为false,则在本地缓冲数据(如果为true),如果存在本地数据,则开始将数据传输到中央服务器
  3. 还会有GUI的更新(顶部的“ONLINE”/“OFFLINE”消息,如果没有连接扫描仪,则无法使用GUI)

    我能够实现这一目标的方法是使用两个持续运行的BackgroundWorkers,轮询设备状态并经常报告。我不确定这种“常量”使用是否是使用BackgroundWorker对象的正确方法。

    在C#WinForms应用程序中是否有更好/更清晰的方法来完成这些背景检查?

2 个答案:

答案 0 :(得分:1)

对于扫描仪界面,最好将逻辑封装在一个或多个类中,并使设备级别的东西远离UI。

网络连接监视器也是如此;这听起来足够复杂,可以进入自己的班级。

如果您正在轮询硬件,最好使用System.Threading.Timer来驱动它而不是BackgroundWorker

这是一个示例控制台应用程序,它有一个简单的实现,逻辑被分成不同的类。

重要的是Timer,它驱动它。它将在一个单独的线程上定期回调,并从中调用poll()。请注意,因为它在一个单独的线程中回调,所以在某些情况下你可能需要使用锁定,而且你不能直接从它调用UI。

对于这个简单的类,bool正在原子操作中更新,因此我们不需要任何同步。

希望你能看到我将多线程逻辑分离成一个单独的类的意思。

using System;
using System.Threading;

namespace Demo
{
    // This represents the interface to the scanner hardware.

    sealed class BarcodeScannerDevice
    {
        int counter;

        public bool IsConnected()
        {
            return (++counter%4) != 0;  // Make it change every 4 calls.
        }
    }

    // This class has the responsibility of polling the scanner device to determine its status.

    sealed class BarcodeScannerMonitor
    {
        readonly Timer timer;
        readonly BarcodeScannerDevice scanner;

        bool isConnected;

        public BarcodeScannerMonitor(BarcodeScannerDevice scanner)
        {
            this.scanner = scanner;
            timer = new Timer(poll, null, Timeout.Infinite, Timeout.Infinite);
        }

        public bool IsConnected
        {
            get
            {
                return isConnected;
            }
        }

        public void StopPolling()
        {
            timer.Change(Timeout.Infinite, Timeout.Infinite);
        }

        public void StartPolling(TimeSpan pollingInterval)
        {
            timer.Change(TimeSpan.Zero, pollingInterval);
        }

        void poll(object state)
        {
            isConnected = scanner.IsConnected();
        }
    }

    static class BarcodeScannerMonitorFactory
    {
        // You probably want to encapsulate this kind of coupling and 
        // keep it away from the UI, so we use a factory.

        public static BarcodeScannerMonitor Create()
        {
            var scanner = new BarcodeScannerDevice();
            return new BarcodeScannerMonitor(scanner);
        }
    }

    class Program
    {
        void run()
        {
            var scannerStatus = BarcodeScannerMonitorFactory.Create();
            scannerStatus.StartPolling(TimeSpan.FromSeconds(1));

            while (true)
            {
                Console.WriteLine("Scanner is " + (scannerStatus.IsConnected ? "connected" : "disconnected"));
                Thread.Sleep(500);
            }
        }

        static void Main()
        {
            new Program().run();
        }
    }
}

另请注意,我没有处理过计时器。正确的代码应该向BarcodeScannerMonitor类添加一个Dispose()方法来处理计时器。

您可能还想为类创建接口,而是使用UI中的接口,这有助于单元测试。

最后,您可能会注意到Microsoft建议将Windows窗体计时器用于Windows窗体程序,但我认为对于这种硬件轮询System.Threading.Timer更好。

答案 1 :(得分:1)

我已经将BackgroundWorkers用于这种类型的应用程序。 BGWorkers非常适合WinForm应用程序,他们共享线程池。 BGWorker只是抽象出更细粒度的线程。当您调用RunWorkerAsync时,将在调用线程中引发DoWork事件。在你的工作线程中,允许调用Sleep;如果您正在检查仪器的状态,那么每1000或2000毫秒的某些内容可能会报告状态,并且不会使GUI充满事件。使用BGWorker中的ReportProgress在GUI线程中引发ProgressChanged事件;你可以在事件args中传递数据。当线程完成时,它会引发RunWorkerCompleted事件。您可以通过将CancellationPending属性设置为true来显式停止线程运行。

这就是设备方面。对于可用的网络连接,您可以想象网络是一个设备并使用相同的一般策略。在我的头脑中,我不清楚我用什么来“ping”网络。