Question on C# threading with RFID

2020-06-27 09:35发布

问题:

My question is for feedback on making sure I am taking the right approach, and handling the threading correctly. I have a feeling I may need to set of some of my own threads, so all feedback is welcome.

The problem I have is reading RFID tags from zero or more RFID readers. I can read for a single reader without an issue so reading from several will not be an issue. Each Tag or batch of Tags read by the reader is delivered by a .Net Event.

My plan is to setup a ReaderControl class, which maintains the readers, connection, starting, stopping, etc. This class will listen to the TagRead events from the readers. On each event it handles (roughly every 250ms) it puts the read tag ids (a string) into a HashSet to keep them unique, the HashSet is located in ReaderControl. The ReaderControl will contain a timer, that fires/elapses every 500ms, this TimerElapsed event is handled by the ReaderControl which will package up the tags read from all readers so far and raise a TagsRead event. The purpose of this is to keep event firing to a minimum and reduce duplicate tags.

The TagsReads event is handled by another class called TagTranslator. This class will loop through the tag ids (strings) and work out what the tag refers to, i.e. IPerson object. This class will fire an event on completion of the translation with a PeopleSeen event.

The PeopleSeen event is handled by a model in a GUI (MVP pattern). The overall idea is a GUI display is shows names of people which pass through the RFID readers. The display is simply but obviously under the hoods tags are being read in asych and been translated to "real" objects to be displayed.

Do you feel ReaderControl should be running on its own thread, I think it should. How do I go about packaging this class in its own thread to just keep reading tags regardless of what the GUI is doing. Also, do you think when the TagTranslator when handling events should create threads to handle the translation.

回答1:

I would suggest that, rather than using events, you use a concurrent queue data structure and a multiple producer, single consumer model. Think of the tag reader threads as producers, and the processing thread as consumers.

When a thread receives a tag from a reader, it adds that tag to the queue, not worrying about duplicates or anything. Heck, you might want that duplicate information at some point. No reason to throw it out right here.

The consumer waits on the queue, taking items off and processing them one at a time.

The BlockingCollection class is perfect for this.

// Shared queue.  Assuming that a tag is simply a string.
BlockingCollection<string> TagQueue = new BlockingCollection<string>();

// Tag reader threads (producers)
while (!ShutdownMessageReceived)
{
    string tag = GetTagFromReader(); // however that's done
    TagQueue.Add(tag);
}

// Processing thread (consumer)
while (!ShutdownMessageReceived)
{
    string tag = TagQueue.Take();
    // process the tag
}

BlockingCollection supports multiple producers and consumers, so you can have as many of each as you like. The Take method will block until an item is available. It's a non-busy wait, so there's no polling overhead.

This kind of thing is very easy to implement with BlockingCollection, and makes for clean and simple code that performs very well.