Click here to Skip to main content
14,669,424 members
Rate this:
Please Sign up or sign in to vote.
See more:
I've written a program which could connect to several RFID Reader and start to detect tags simultaneously. But because all of them are working on UI-Thread (Main Thread) my both UI and reading function become too slow every time I add a new reader...
I need to create a new thread for each reader and let that reader works with the new worker thread.
But I don't know where have I import the new Thread() function in my c# program.
I have a windows form timer for reading tags.

What I have tried:

 void timer_Tick(object sender, EventArgs e)
        {
            Reader();
        }

Public void Reader(){
int fCmdRet = StaticClassReaderB.Inventory_G2(ref fComAdr, Qvalue, Session, AdrTID, LenTID, TIDFlag, EPC, ref Totallen, ref CardNum, frmcomportindex);

                if ((fCmdRet == 1) | (fCmdRet == 2) | (fCmdRet == 3) | (fCmdRet == 4) | (fCmdRet == 0xFB))//The search is over
                {
                    if (CardNum == 0)
                    {
                        return;
                    }
                    byte[] daw = new byte[Totallen];
                    Array.Copy(EPC, daw, Totallen);
                    temps = ByteArrayToHexString(daw);
                    //fInventory_EPC_List = temps;            //Store records
                    if (temps == "") return;
                    m = 0;
                    for (CardIndex = 0; CardIndex < CardNum; CardIndex++)
                    {
                        EPClen = daw[m];
                        string TID = temps.Substring(m * 2 + 2, EPClen * 2);
                        lastEPC = TID;
                        m = m + EPClen + 2;
                        value = TID;
                        //OnScan(this, new EventArgs());
                    }
               }


I have two class as below:
public partial class RfControl : UserControl
    {
        RfReader rf = new RfReader();
        public RfControl()
        {
            InitializeComponent();
        }

        private void btnOpen_Click(object sender, EventArgs e)
        {
            try
            {
                rf.type = this.cbType.Text;
                rf.IPAddr = this.txtIP.Text.Trim();
                rf.port = Convert.ToInt32(this.txtPort.Text.Trim());
                rf.OnScan += new EventHandler(rf_OnScan);
                rf.Open();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        void rf_OnScan(object sender, EventArgs e)
        {
            try
            {
                string value = ((RfReader)sender).value;
                if (this.lstContent.Items.IndexOf(value) == -1)
                {
                    this.lstContent.Items.Add(value);
                }
                this.txtCount.Text = this.lstContent.Items.Count.ToString();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void btnClose_Click(object sender, EventArgs e)
        {
            try
            {
                rf.Close();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void btnClear_Click(object sender, EventArgs e)
        {
            this.lstContent.Items.Clear();
            this.txtCount.Text = "0";
        }

        private void btnQuery_Click(object sender, EventArgs e)
        {
           rf.timer.Enabled = !rf.timer.Enabled;
        }
}

class RfReader
    {
        Api api = new Api();
        ArrayList idList = new ArrayList();
        byte[,] tagData = null;
        byte v1 = 0;
        byte v2 = 0;
        int status = 0;
        int iMembankMask = 0, iStartAddr_Reserve = 0, iStartAddr_EPC, iStartAddr_TID, iStartAddr_User, reserveLen, epcLen, tidLen, userLen, readCnt, wordLen = 0;
        public int frmcomportindex;
        public byte readerAddr = 0xff;
        private byte fComAdr = 0xff;
        public int port = -1, openresult = 0;
        public string IPAddr = "";
        public string type = "";
        public string value = "";
        public string X = "";
        public string Y = "";
        public string Name = "";
        public System.Windows.Forms.Timer timer = null;
        private static object _locker = new object();
        public RfReader()
        {
            timer = new System.Windows.Forms.Timer();
            timer.Interval = 1;
            timer.Tick += new EventHandler(timer_Tick);
          
        }
        void timer_Tick(object sender, EventArgs e)
        {
            ThreadPool.QueueUserWorkItem((object state) => { Reader(); });
                
            
        }
        public void Open()
        {
            if (type == "AR")
            {
                if (!api.isNetWorkConnect(IPAddr))
                {
                    api.TcpCloseConnect();
                    throw new Exception(IPAddr + " Open failed");
                }
                status = api.TcpConnectReader(IPAddr, Convert.ToInt32(port));
                if (status != ReaderApi.Api.SUCCESS_RETURN)
                {
                    api.TcpCloseConnect();
                    throw new Exception(IPAddr + " Open failed");
                }
                status = api.GetFirmwareVersion(255, ref v1, ref v2);
                if (status != ReaderApi.Api.SUCCESS_RETURN)
                {
                    api.TcpCloseConnect();
                    throw new Exception(IPAddr + " Open failed");
                }
                iStartAddr_Reserve = iStartAddr_EPC = iStartAddr_TID = iStartAddr_User = reserveLen = epcLen = tidLen = userLen = readCnt = 0;
                iMembankMask = 0;
                wordLen = 6;
                iMembankMask += 1;
                wordLen += 4;
                iStartAddr_Reserve = 0;
                reserveLen = 4;
                iMembankMask += 2;
                wordLen += 8;
                iStartAddr_EPC = 0;
                epcLen = 8;
                iMembankMask += 4;
                wordLen += 12;
                iStartAddr_TID = 0;
                tidLen = 12;
                iMembankMask += 8;
                wordLen += 10;
                iStartAddr_User = 0;
                userLen = 10;
                tagData = new byte[500, wordLen * 4];
                idList.Clear();
                throw new Exception(IPAddr + " Open Success");
            }
            else if (type == "ZK")
            {
                byte readerAddr = Convert.ToByte("FF", 16); // $FF;
                openresult = StaticClassReaderB.OpenNetPort(port, IPAddr, ref fComAdr, ref frmcomportindex);
                if ((openresult == 0x35) || (openresult == 0x30))
                {
                    StaticClassReaderB.CloseNetPort(frmcomportindex);
                    throw new Exception(IPAddr + " Open failed");
                }
                if (openresult == 0)
                {
                    throw new Exception(IPAddr + " Open Success");
                }
            }
        }
        public void Close()
        {
            if (type == "AR")
            {
                api.TcpCloseConnect();
            }
            else if (type == "ZK")
            {
                StaticClassReaderB.CloseNetPort(frmcomportindex);
                frmcomportindex = -1;
            }
        }
        public event EventHandler OnScan;
        public void Reader()
        {
            if (type == "AR")
            {
                readCnt = 0;
                int getCount = 0;
                string strTemp = "", strAnteNo = "", strEPC = "", strSubEPC = "", strTID = "", strReserve = "", strUser = "";
                status = api.Gen2MultiTagRead(255, (byte)iMembankMask, (byte)iStartAddr_Reserve, (byte)reserveLen, (byte)iStartAddr_EPC, (byte)epcLen, (byte)iStartAddr_TID, (byte)tidLen, (byte)iStartAddr_User, (byte)userLen, ref readCnt);

                if (status == Api.SUCCESS_RETURN && readCnt > 0)
                {
                    if (api.GetTagData(255, ref tagData, readCnt, ref getCount) == Api.SUCCESS_RETURN && getCount > 0)
                    {
                        for (int loop = 0; loop < getCount; loop++)
                        {
                            strTemp = "";
                            for (int j = 1; j <= (int)tagData[loop, 0]; j++)
                                strTemp += string.Format("{0:X2}", tagData[loop, j]);
                            strEPC = strTemp.Substring(2, 24);
                            strReserve = strTemp.Substring(26, reserveLen * 4);
                            strSubEPC = strTemp.Substring(26 + reserveLen * 4, epcLen * 4);
                            value = strTemp.Substring((26 + reserveLen * 4 + epcLen * 4) + 8, 16);
                            strUser = strTemp.Substring(26 + reserveLen * 4 + epcLen * 4 + tidLen * 4, userLen * 4);
                            OnScan(this, new EventArgs());
                        }
                    }
                }
            }
            else if (type == "ZK")
            {
                
                int CardNum = 0, Totallen = 0, EPClen, m, CardIndex;
                byte[] EPC = new byte[5000];
                string temps, s;
                byte AdrTID = 0, LenTID = 0, TIDFlag = 0;
                AdrTID = 2;
                LenTID = 4;
                TIDFlag = 1;
                byte Qvalue = 4;
                byte Session = 0;
                string lastEPC = "";
                int fCmdRet = StaticClassReaderB.Inventory_G2(ref fComAdr, Qvalue, Session, AdrTID, LenTID, TIDFlag, EPC, ref Totallen, ref CardNum, frmcomportindex);

                if ((fCmdRet == 1) | (fCmdRet == 2) | (fCmdRet == 3) | (fCmdRet == 4) | (fCmdRet == 0xFB))//The search is over
                {
                    if (CardNum == 0)
                    {
                        return;
                    }
                    byte[] daw = new byte[Totallen];
                    Array.Copy(EPC, daw, Totallen);
                    temps = ByteArrayToHexString(daw);
                    //fInventory_EPC_List = temps;            //Store records
                    if (temps == "") return;
                    m = 0;
                    for (CardIndex = 0; CardIndex < CardNum; CardIndex++)
                    {
                        EPClen = daw[m];
                        string TID = temps.Substring(m * 2 + 2, EPClen * 2);
                        lastEPC = TID;
                        m = m + EPClen + 2;
                        value = TID;
                        //OnScan(this, new EventArgs());
                    }
                }
                //else
                //{
                //    return;
                //}

            }
        }

i jsut added new functions as below instead of that timer :
private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
 {
     while (true)
     {
         rf.Reader();
     }

 }

private void btnQuery_Click(object sender, EventArgs e)
{
        backgroundWorker1.RunWorkerAsync();
}
Posted
Updated 20-Sep-20 3:55am
v8

Rate this:
Please Sign up or sign in to vote.

Solution 1

I sure hope I understood your question correctly. Here goes:

First of all, I'd consider using the Task API as opposed to a thread directly

Inside your timer_Tick() function

// make sure to do "using System.Threading.Tasks;"
Task.Run(()=>{Reader();});


Another decent option is using the threadpool

// make sure to do "using System.Threading;"
ThreadPool.QueueUserWorkItem((state)=>{Reader();});


Another option is to use the BackgroundWorker component that is part of the winforms stuff. I personally don't like using it because it's heavy handed but to each their own

Using Thread directly, particularly in a Winforms app isn't such a great idea since running foreground threads will prevent your app from exiting when the main form is closed. In your case it doesn't matter since Reader() is relatively speaking, short running but I'm trying to show you for other scenarios as well.

One caveat is that since Reader() is now fired from another thread you must not communicate with the form or its controls directly since they are not thread safe.

Here's how marshal any user interface updates inside Reader() back to the main thread using message passing:
// anything inside the anonymous method will be executed on the UI thread:
this.Invoke(new Action(()=>{ ++myProgressBar.Value;}));


One potential problem with your code:
It looks like you're using ref inside your reader routine and passing in member fields from the form. This is not thread safe. If you have to modify member variables on the form you must synchronize access to them! You can use the this.Invoke technique to modify them from the main thread. There are better ways but teaching them here would make this answer way too long

Anyway, I hope that helps!
   
v5
Comments
Mohammad Razmi 20-Sep-20 4:48am
   
Both methods which you provided just unable me to read from multi readers at the same time.
moreover, when one reader is reading tags and a disconnection occurs both methods you provided try to create new threads over and over and it makes my program get stuck...
honey the codewitch 20-Sep-20 5:11am
   
It looks like you're going to have to modify your code extensively then because your reader routine uses member fields from the looks of it. That means one reader per form which is not what you want. You should be putting those members you are using in a class, and using one class instance per reader call. Something like that anyway. I can't see enough of your code to be sure exactly what you're doing there but basically, you have a design issue from the looks of it. Your member variables are one per form, and i think you need them to be one per reader.
Mohammad Razmi 20-Sep-20 5:19am
   
I just added my two classes
see them up
honey the codewitch 20-Sep-20 5:26am
   
Let me see if I can untangle this code and figure out where your per instance data needs to be. I might not have a solution for you right away, since it's 2:30am and I need to go to sleep again at some point but I'll try to figure this out based on what you posted.

Edit: Can you use more than one instance of RfReader() at a time? if you can then I may modify the code to create one RfReader() per call and then eliminate the timer call from RfReader() entirely. If you spin a loop in your thread you won't need a timer anyway.
Mohammad Razmi 22-Sep-20 7:11am
   
each form is related to just one reader...
so there is no difference between one instance per form or reader
honey the codewitch 22-Sep-20 10:31am
   
And that's what you want? Well, since your code is all jacked up anyway, maybe try creating the entire form on its own thread.

ThreadPool.QueueUserWorkItem((state)=>{Application.Run(new myForm());});

Which may or may not work.

Honestly I'd never accept your code if I was reviewing it. So if this is for work? Rewrite it. It could get you fired.
Mohammad Razmi 22-Sep-20 10:40am
   
no, it's not for my work...
it's for my Masters's thesis...
for god's sake give me your email address and let me send you the entire solution...
honey the codewitch 22-Sep-20 10:46am
   
i don't have the hardware to run it. and I can't rewrite everything without it which is the first thing to do.

for code to be multithreaded it has to be well encapsulated. You have no consistent encapsulation from what I've seen. every member is whatever you thought you'd need at the time, and where's the planning?

sit down, plan it, then write it. consider your first attempt a draft.
Mohammad Razmi 22-Sep-20 10:49am
   
I just want you to see all of my codes as a solution for more understanding, not anything else.
I even can share my desktop with Anydesk.
honey the codewitch 22-Sep-20 10:52am
   
okay. i'll look at it. here's a temporary email address i'll look for it from

honeythemonster@mail.com
Mohammad Razmi 24-Sep-20 4:26am
   
I'm still waiting for it.
honey the codewitch 24-Sep-20 5:56am
   
Yeah, it turns out, it needs an entire rewrite.

Your code isn't usable. So I'm trying to reverse engineer the Api you have for hardware I don't even own. Be patient
Mohammad Razmi 24-Sep-20 6:01am
   
ok Thank you thou
Mohammad Razmi 22-Sep-20 10:50am
   
I'm not that much professional in programming.
I just started programming for 2 months
honey the codewitch 22-Sep-20 10:53am
   
And multithreading is not really something you want to be doing after two months.

It's like doing an engine/motor swap on a car right after learning to change the tyres/tires

I'm going to see if i can rig up something for you that avoids it
Mohammad Razmi 22-Sep-20 11:06am
   
Yes, I know...
but I have to do this...
I just sent you an email.
thank you very much.
Mohammad Razmi 20-Sep-20 7:50am
   
Yes, I could create as many as an instance of the RfReader() class.
I need a timer because every loop ends when it's condition finish but I need a non-ending loop for it.
honey the codewitch 20-Sep-20 9:43am
   
while(true) {
// do work
}

Inside that loop you might try creating the RFReader() and using it, and then destroying it each time.
Rate this:
Please Sign up or sign in to vote.

Solution 2

To add to the possibilities Honey the codewitch has mentioned a version which is pretty simple for "threading novices" to use is the BackgroundWorker Class[^] - it's event driven, and provides progress reporting back to the UI thread for updates via it's ProgressChanged event.
This would allow you to set up a worker thread for each reader, which signals back to the UI thread when an RFID device is scanned, including the RFID data via the ProgressChangedEventArgs.UserState property[^].
   
Comments
honey the codewitch 20-Sep-20 4:45am
   
I mentioned it, but it's actually more code to use it, which is one of the reasons I don't like it. More code = more potential for bugs, and frankly, BackgroundWorker kind of obscures things a little too much for my taste. Just my $0.02 but I don't think the class should even exist.
OriginalGriff 20-Sep-20 5:05am
   
It's more code to set up, yes - but it's less code to use for a novice who wants to update UI stuff. And it's all "familiar code" - events and handlers - which makes it a lot easier to understand what is going on.
And since it's a Background thread it automatically gets terminated when the app dies - a foreground thread has to be terminated before the process is unloaded from the system (another mistake rookies often make!)
honey the codewitch 20-Sep-20 5:10am
   
It is familiar, but it's also a pain to demonstrate the use of it in this format because there's just a lot to hooking it up. And every method I gave him spawns a background thread. Either way, it looks like his issue is a design one since he is using member variables in Reader() and wants multiple instances of those variables (1 per reader instead of 1 per form)
Mohammad Razmi 20-Sep-20 4:46am
   
could you please more explain by clarifying where have I import the class in my project?
and how to use it?
when press the open button or when enable the timer?
OriginalGriff 20-Sep-20 5:08am
   
You don't involve a Timer at all: you set up a thread that handles a single reader and start that off when your app starts. When it reads an RFID code, it reports back to the UI thread with the ID information and goes back to waiting for another RFID activation.
Since each RFID reader has it's own thread, they don't mess with each other, or the UI thread.
Mohammad Razmi 20-Sep-20 8:45am
   
I tried to understand and use the background worker class in my program.
it almost fixed my issues
but I still have a problem.
when one more reader adds to my program other readers function inorder to reading tags become a little slower than before.
what do for it?
OriginalGriff 20-Sep-20 9:10am
   
Without seeing your code and knowing exactly what you have done, I can't answer that.
And frankly, your questions don't exactly inspire confidence in what you think you have achieved - I'm not convinced you understand enough about threading to have used it correctly.

The idea is that each physical reader has its own thread, which it keeps in perpetuity and sits there doing nothing waiting for an RFID device to show up. What I'd suspect you are doing is moving your timer into a thread and not really thinking too hard about how this is supposed to work.
Start with one reader, and get it working in a thread with no timers so that you can present a tag, get a reading, display it on your UI repeatedly. If your code and the API are written correctly, it should be doing nothing at all most of the time!
Mohammad Razmi 20-Sep-20 9:19am
   
my problem is not actually the UI reporting of Tags TID.
the TID and what a reader returns as a result will be stored in a database (SQL Server compact).
my problem is just the reading and reconnecting of those readers which I need to be more smooth without any delay in the reading function of tags.
which part of my code do you need more than what I posted above?
Mohammad Razmi 21-Sep-20 3:29am
   
I just added my code above.
do I have to create one background worker per reader or just one for all of them?
OriginalGriff 21-Sep-20 3:38am
   
One per reader - but not in a timer! All you are doing is adding more and more threads to the system every time the timer ticks.

Please, go and do some reading on threading - you have just taken existing (bad) code and stuffed threading into it, and that won't work.
You have to understand what threading is, and how it works, then design your code to work with threads.

Just trying to bolt it onto poor code isn't going to work any better than using epoxy glue to attach six jet engines to the roof of your car and hoping it will work perfectly ...
Mohammad Razmi 21-Sep-20 4:35am
   
I just changed my program to create a bgWorker for each reader.
I removed my timer.
I use a while loop for reading.
but still every time I add a new reader and it starts to read tags despite it is working with a new working thread ID but the reading functionality of other readers become slower
honey the codewitch 21-Sep-20 9:46am
   
It could be a limitation of your device. Bandwidth isn't infinite. Or it might be somewhere else in your code, who knows? Frankly your code is a mess, and I can't even make enough sense of it to tell you that what you're doing now is thread safe. That's why I suggested a rewrite in my other reply.
Mohammad Razmi 20-Sep-20 8:47am
   
furthermore, I used a for loop instead of a timer.
it has a risk that after 1000000000 times read my program stop working.
how can I create a non-ends for loop or another way to do this?
honey the codewitch 20-Sep-20 9:41am
   
while(true) {
// do work
}
Mohammad Razmi 20-Sep-20 9:48am
   
ops :)) yes, thank you I forgot it...
what about what I told above in order to make my program more responsive and working smoothly in case of adding a new reader
honey the codewitch 20-Sep-20 9:55am
   
I think you need to take the timer or loop out of RfReader()
I think you need to move it into your loop

while(true) {
RFReader r = new RFReader();
r.Read(); // you'll have to add this method
r.Close()
}

Like that. I still have no idea what you're doing with the results of it, but I imagine whatever it is it's probably not thread safe anymore
honey the codewitch 20-Sep-20 9:57am
   
adding, I think your app has design problems that are well beyond the scope of this Question and Answer forum.
Mohammad Razmi 21-Sep-20 3:30am
   
any answer?
honey the codewitch 21-Sep-20 8:58am
   
Yeah, but you're not going to like it: Go back, reread the documentation for the RFID equipment, and then rewrite the code entirely.
OriginalGriff 21-Sep-20 9:09am
   
Why do I get the feeling he isn't listening to a thing we tell him? :sigh:
honey the codewitch 21-Sep-20 9:15am
   
I don't want to be harsh, but that code is very difficult to read, and looks like even if it was "working" it would crash due to a race condition. I just can't make something whole with what I was given.
Mohammad Razmi 21-Sep-20 9:44am
   
OriginalGriff I'm listening to you. I'm not that professional in programming and I just started it...
could you please just do me a favor?
could any of you two give me an email and let me send my project, please. I know its a conflict with the rolls but I'm tired of searching for this threading problem...
Mohammad Razmi 21-Sep-20 9:45am
   
OriginalGriff I'm listening to you. I'm not that professional in programming and I just started it...
could you please just do me a favor?
could any of you two give me an email and let me send my project, please. I know its a conflict with the rolls but I'm tired of searching for this threading problem...
honey the codewitch 21-Sep-20 9:50am
   
Part of your problem is you're trying to run before you can walk. Multithreading is one of the most difficult programming problems. Your code isn't clean, isn't readable, and isn't properly encapsulated, so you can't build it up without it falling over under its own mess. Trying to make it multithreaded is just going to make anything wrong with it that much worse.

That's why I suggested the rewrite after the first couple of things didn't work.
Mohammad Razmi 20-Sep-20 10:10am
   
actually my problem is not the UI design or s.th depending on UI.
the results of the read function will be stored in a SQL Compact server and I truly don't need the result to be shown on the UI.
I just need to create a program in which be okay with adding as many as readers I need and works responsively.
it means in cases of adding new reader to the network or disconnection of those readers the reading function of other readers do not affect with it.just for this reason I have to try to separate the thread of each reader.
honey the codewitch 24-Sep-20 10:54am
   
I found the problem I think. I can't test it though. I think your bottleneck is either your USB or COM port (whichever you are using).

I can keep a form responsive for you, but as you said that doesn't matter. What matters is you need to be able to submit to a database while other card reads are occurring.

Here's your problem - you don't have enough bandwidth. If it's USB, you need more than one bus. If it's a COM port you're going to need a lot more of them if you want to do a lot of concurrent reads.

Going through your code, indeed, firing the Reader() method on a separate thread will keep the form responsive but it will not make the reader hardware more responsive.

Especially if you're using a COM port, there simply isn't enough bandwidth
Mohammad Razmi 24-Sep-20 11:58am
   
there is no USB/com. I'm using a cisco router and my readers are connected with my laptop via Network.
the problem is not the bandwidth because when I open one program per reader they all work perfectly
honey the codewitch 24-Sep-20 12:07pm
   
okay, fair enough. i'm having trouble tracking down where the bottleneck is then.

If calling Reader() from another thread didn't solve it, and if the bottleneck is not that Api() stuff you're doing (which looks a whole lot like a COM port read) then I don't know where your bottleneck is in your code, and since I cannot run the code I cannot find that out.
Mohammad Razmi 24-Sep-20 12:09pm
   
I could share my desktop with remote applications and you can test whatever you want
honey the codewitch 24-Sep-20 13:05pm
   
I'm not going to remote desktop with you, sorry. I don't have any more time for this right now anyway.
Mohammad Razmi 24-Sep-20 13:06pm
   
Okay thank you and your time

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)




CodeProject, 503-250 Ferrand Drive Toronto Ontario, M3C 3G8 Canada +1 416-849-8900 x 100