Mohammad Razmi Ответов: 2

Многопоточность C# в RFID-считывателях


Я написал программу, которая могла бы подключиться к нескольким RFID - считывателям и начать обнаруживать метки одновременно. Но поскольку все они работают на UI-Thread (Main Thread), мой пользовательский интерфейс и функция чтения становятся слишком медленными каждый раз, когда я добавляю новый читатель...
Мне нужно создать новый поток для каждого читателя и позволить этому читателю работать с новым рабочим потоком.
Но я не знаю, где я импортировал новую функцию Thread() в свою программу на c#.
У меня есть таймер windows form для чтения тегов.

Что я уже пробовал:

 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());
                    }
               }


У меня есть два класса, как показано ниже:
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;
                //}

            }
        }

я jsut добавил новые функции как показано ниже вместо этого таймера :
private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
 {
     while (true)
     {
         rf.Reader();
     }

 }

private void btnQuery_Click(object sender, EventArgs e)
{
        backgroundWorker1.RunWorkerAsync();
}

2 Ответов

Рейтинг:
1

honey the codewitch

Я очень надеюсь, что правильно понял ваш вопрос. Здесь идет:

Прежде всего, я бы рассмотрел возможность использования API задач, а не непосредственно потока

Внутри вашей функции timer_Tick()

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


Еще один достойный вариант-использование threadpool

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


Другой вариант - использовать BackgroundWorker компонент, который является частью материала winforms. Я лично не люблю его использовать потому что он тяжеловесный но каждому свое

С помощью Нить непосредственно, особенно в приложении Winforms, это не такая уж отличная идея, поскольку запуск потоков переднего плана предотвратит выход вашего приложения из основной формы. В вашем случае это не имеет значения, так как Читатель() это, условно говоря, короткая работа, но я пытаюсь показать вам и другие сценарии.

Одно предостережение заключается в том, что с тех пор Читатель() теперь вы не должны напрямую связываться с формой или ее элементами управления, так как они не являются потокобезопасными.

Вот как Маршалл любой пользовательский интерфейс обновляется внутри Читатель() вернуться к основному потоку с помощью передачи сообщений:
// anything inside the anonymous method will be executed on the UI thread:
this.Invoke(new Action(()=>{ ++myProgressBar.Value;}));


Одна потенциальная проблема с вашим кодом:
Похоже, что вы используете ref внутри своей процедуры чтения и передаете поля-члены из формы. Это не потокобезопасно. Если вам нужно изменить переменные-члены в форме, вы должны синхронизировать доступ к ним! Вы можете использовать это.Вызовите технику, чтобы изменить их из основного потока. Есть и лучшие способы но преподавание их здесь сделало бы этот ответ слишком длинным

Во всяком случае, я надеюсь, что это поможет!


Mohammad Razmi

Оба метода, которые вы предоставили, просто не позволяют мне читать из нескольких читателей одновременно.
более того, когда один читатель читает теги и происходит разъединение, оба метода, которые вы предоставили, пытаются создавать новые потоки снова и снова, и это заставляет мою программу застревать...

honey the codewitch

Похоже, что вам придется сильно изменить свой код, потому что ваша процедура чтения использует поля-члены, судя по всему. Это означает, что на каждую форму приходится по одному читателю, а это совсем не то, что вам нужно. Вы должны помещать те члены, которые вы используете, в класс и использовать один экземпляр класса для каждого вызова reader. Во всяком случае, что-то в этом роде. Я не могу видеть достаточно вашего кода, чтобы точно знать, что вы там делаете, но в основном у вас есть проблема с дизайном, судя по всему. Ваши переменные-члены-это по одной на форму, и я думаю, что вам нужно, чтобы они были по одной на читателя.

Mohammad Razmi

Я только что добавил свои два класса
смотрите, как они поднимаются

honey the codewitch

Позвольте мне посмотреть, смогу ли я распутать этот код и выяснить, где должны быть ваши данные для каждого экземпляра. Возможно, у меня нет решения для вас прямо сейчас, так как сейчас 2:30 утра, и мне нужно снова лечь спать в какой-то момент, но я попытаюсь выяснить это, основываясь на том, что вы написали.

Edit: можете ли вы использовать более одного экземпляра RfReader() одновременно? если вы можете, то я могу изменить код, чтобы создать один RfReader() на вызов, а затем полностью исключить вызов таймера из RfReader (). Если вы закрутите петлю в своей нити, вам все равно не понадобится таймер.

Mohammad Razmi

каждая форма связана только с одним читателем...
таким образом, нет никакой разницы между одним экземпляром на форму или читателя

honey the codewitch

И это то, чего ты хочешь? Ну, поскольку ваш код все равно весь измучен, может быть, попробуйте создать всю форму в своем собственном потоке.

Класса ThreadPool.Метод queueuserworkitem((казенная)=&ГТ;{приложение.Бег(новые ссылки());});

Что может сработать, а может и не сработать.

Честно говоря, я бы никогда не принял ваш код, если бы просматривал его. Так что, если это для работы? Переписать его. Из-за этого тебя могут уволить.

Mohammad Razmi

нет, это не для моей работы...
это для моей магистерской диссертации...
ради Бога, дайте мне ваш адрес электронной почты и позвольте мне отправить вам все решение...

honey the codewitch

у меня нет оборудования, чтобы запустить его. и я не могу переписать все без него, что и нужно сделать в первую очередь.

чтобы код был многопоточным, он должен быть хорошо инкапсулирован. У вас нет последовательной инкапсуляции из того, что я видел. каждый член-это то, что вы думали, что вам нужно в то время, и где планирование?

садись, планируй, а потом пиши. считайте свою первую попытку черновиком.

Mohammad Razmi

Я просто хочу, чтобы вы рассматривали все мои коды как решение для большего понимания, а не что-то еще.
Я даже могу поделиться своим рабочим столом с Anydesk.

honey the codewitch

окей. я посмотрю на него. вот временный адрес электронной почты, с которого я буду его искать

honeythemonster@mail.com

Mohammad Razmi

Я все еще жду этого.

honey the codewitch

Да, оказывается, его нужно полностью переписать.

Ваш код не может быть использован. Вот я и пытаюсь вычислить по API у вас есть для аппаратные меня даже нет собственного. Будьте терпеливы

Mohammad Razmi

хорошо спасибо тебе

Mohammad Razmi

Я не настолько профессионален в программировании.
Я только начал программировать в течение 2 месяцев

honey the codewitch

А многопоточность-это не совсем то, чем вы хотите заниматься через два месяца.

Это все равно что делать замену двигателя/двигателя на автомобиле сразу после того, как вы научитесь менять шины/шины

Я собираюсь посмотреть, смогу ли я соорудить для вас что-то, что позволит избежать этого

Mohammad Razmi

- Да, я знаю...
но я должен это сделать...
Я только что отправил тебе письмо.
Большое спасибо!.

Mohammad Razmi

Да, я мог бы создать столько же экземпляров класса RfReader ().
Мне нужен таймер, потому что каждый цикл заканчивается, когда он закончен, но мне нужен нескончаемый цикл для него.

honey the codewitch

в то время как(правда) {
// делать работу
}

Внутри этого цикла вы можете попробовать создать RFReader() и использовать его, а затем каждый раз уничтожать его.

Рейтинг:
1

OriginalGriff

Чтобы добавить к этим возможностям мед, codewitch упомянул версию, которая довольно проста для использования "новичками в потоковой передаче", это Класс BackgroundWorker[^] - он управляется событиями и предоставляет отчеты о ходе выполнения обратно в поток пользовательского интерфейса для обновления через событие ProgressChanged.
Это позволит вам настроить рабочий поток для каждого считывателя, который сигнализирует обратно в поток пользовательского интерфейса при сканировании RFID-устройства, включая данные RFID через ProgressChangedEventArgs.Свойство UserState[^].


honey the codewitch

Я упоминал об этом, но на самом деле это больше кода, чтобы использовать его, и это одна из причин, почему он мне не нравится. Больше кода = больше возможностей для ошибок, и, честно говоря, BackgroundWorker немного затеняет вещи, на мой вкус. Только мои 0,02 доллара, но я не думаю, что этот класс вообще должен существовать.

OriginalGriff

Это больше кода для настройки, да, но это меньше кода для использования новичком, который хочет обновить пользовательский интерфейс. И это все "знакомый код" - события и обработчики, что значительно облегчает понимание того, что происходит.
А поскольку это фоновый поток, он автоматически завершается, когда приложение умирает - поток переднего плана должен быть завершен до того, как процесс будет выгружен из системы (еще одна ошибка, которую часто совершают новички!)

honey the codewitch

Это знакомо, но это также боль, чтобы продемонстрировать его использование в этом формате, потому что есть просто много, чтобы подключить его. И каждый метод, который я ему дал, порождает фоновую нить. В любом случае, похоже, что его проблема связана с дизайном, так как он использует переменные-члены в Reader() и хочет иметь несколько экземпляров этих переменных (1 на читателя вместо 1 на форму)

Mohammad Razmi

не могли бы вы подробнее объяснить, уточнив, куда я импортировал класс в своем проекте?
и как им пользоваться?
когда нажимаете кнопку Открыть или когда включаете таймер?

OriginalGriff

Вы вообще не используете таймер: вы настраиваете поток, который обрабатывает один читатель, и запускаете его при запуске вашего приложения. Когда он считывает RFID-код, он сообщает обратно в поток пользовательского интерфейса с идентификационной информацией и возвращается к ожиданию другой активации RFID.
Поскольку каждый RFID-считыватель имеет свой собственный поток, они не связываются друг с другом или с потоком пользовательского интерфейса.

Mohammad Razmi

Я попытался понять и использовать класс background worker в своей программе.
это почти исправило мои проблемы
но у меня все еще есть проблема.
когда еще один читатель добавляет в мою программу другие читатели функционируют в порядке, чтобы чтение тегов стало немного медленнее, чем раньше.
что же для этого делать?

OriginalGriff

Не видя вашего кода и не зная точно, что вы сделали, я не могу ответить на этот вопрос.
И, честно говоря, ваши вопросы не совсем внушают уверенность в том, чего, по вашему мнению, вы достигли - я не уверен, что вы достаточно разбираетесь в потоках, чтобы правильно их использовать.

Идея состоит в том, что каждый физический считыватель имеет свою собственную нить, которую он держит в вечности и сидит там, ничего не делая, ожидая появления RFID-устройства. Я подозреваю, что вы перемещаете свой таймер в поток и на самом деле не слишком задумываетесь о том, как это должно работать.
Начните с одного считывателя и заставьте его работать в потоке без таймеров, чтобы вы могли представить тег, получить чтение, повторно отобразить его в своем пользовательском интерфейсе. Если ваш код и API написаны правильно, он должен вообще ничего не делать большую часть времени!

Mohammad Razmi

моя проблема на самом деле не в том, что пользовательский интерфейс сообщает о тегах TID.
TID и то, что читатель возвращает в результате, будут сохранены в базе данных (SQL Server compact).
моя проблема заключается только в чтении и повторном подключении тех считывателей, которые мне нужны, чтобы быть более плавными без каких-либо задержек в функции чтения тегов.
какая часть моего кода вам нужна больше, чем то, что я опубликовал выше?

Mohammad Razmi

Я только что добавил свой код выше.
должен ли я создать один фоновый рабочий для каждого читателя или только один для всех них?

OriginalGriff

По одному на каждого читателя - но не в таймере! Все, что вы делаете, это добавляете все больше и больше потоков в систему каждый раз, когда тикает таймер.

Пожалуйста, пойдите и почитайте немного о потоковой передаче - вы только что взяли существующий (плохой) код и запихнули в него потоковую передачу, и это не сработает.
Вы должны понять, что такое потоковая обработка и как она работает, а затем разработать свой код для работы с потоками.

Просто попытка прикрутить его к плохому коду не будет работать лучше, чем использовать эпоксидный клей, чтобы прикрепить шесть реактивных двигателей к крыше вашего автомобиля и надеяться, что он будет работать идеально ...

Mohammad Razmi

Я просто изменил свою программу, чтобы создать bgWorker для каждого читателя.
Я убрал свой таймер.
Я использую цикл while для чтения.
но все же каждый раз когда я добавляю новый ридер и он начинает читать теги несмотря на то что он работает с новым идентификатором рабочего потока но функциональность чтения других ридеров становится медленнее

honey the codewitch

Это может быть ограничение вашего устройства. Пропускная способность не бесконечна. Или это может быть где-то еще в вашем коде, кто знает? Честно говоря, ваш код-это беспорядок, и я даже не могу понять его достаточно, чтобы сказать вам, что то, что вы делаете сейчас, является потокобезопасным. Вот почему я предложил переписать в своем другом ответе.

Mohammad Razmi

кроме того, я использовал цикл for вместо таймера.
есть риск, что после 1000000000 раз прочитанного моя программа перестанет работать.
как я могу создать цикл non-ends for или другой способ сделать это?

honey the codewitch

в то время как(правда) {
// делать работу
}

Mohammad Razmi

ОПС :)) да, спасибо, я его забыл...
как насчет того, что я сказал выше, чтобы сделать мою программу более отзывчивой и плавно работающей в случае добавления нового читателя

honey the codewitch

Я думаю, что вам нужно вынуть таймер или цикл из RfReader()
Я думаю, что вам нужно переместить его в свою петлю

в то время как(правда) {
RFReader r = новый RFReader();
r.Read(); // вам придется добавить этот метод
Р.Рядом()
}

Как это. Я до сих пор понятия не имею, что вы делаете с его результатами, но я предполагаю, что бы это ни было, это, вероятно, больше не является потокобезопасным

honey the codewitch

добавляя, Я думаю, что у вашего приложения есть проблемы с дизайном, которые выходят далеко за рамки этого форума вопросов и ответов.

Mohammad Razmi

есть ответ?

honey the codewitch

Да, но вам это не понравится: вернитесь назад, перечитайте документацию на RFID-оборудование, а затем полностью перепишите код.

OriginalGriff

Почему у меня такое чувство, что он не слушает ничего из того, что мы ему говорим? :вздыхать:

honey the codewitch

Я не хочу быть резким, но этот код очень трудно читать, и похоже, что даже если бы он "работал", то потерпел бы крах из-за состояния гонки. Я просто не могу сделать что-то цельное из того, что мне дали.

Mohammad Razmi

Оригинальный Грифф, я слушаю тебя. Я не настолько профессионален в программировании, и я только начал это делать...
не могли бы вы просто сделать мне одолжение?
не мог бы кто-нибудь из вас двоих дать мне электронное письмо и позволить отправить мой проект, пожалуйста. Я знаю, что это конфликт с рулонами, но я устал искать эту проблему резьбы...

Mohammad Razmi

Оригинальный Грифф, я слушаю тебя. Я не настолько профессионален в программировании, и я только начал это делать...
не могли бы вы просто сделать мне одолжение?
не мог бы кто-нибудь из вас двоих дать мне электронное письмо и позволить отправить мой проект, пожалуйста. Я знаю, что это конфликт с рулонами, но я устал искать эту проблему резьбы...

honey the codewitch

Часть твоей проблемы в том, что ты пытаешься бежать, прежде чем научишься ходить. Многопоточность-одна из самых сложных проблем программирования. Ваш код не чист, не читаем и не инкапсулирован должным образом, поэтому вы не можете построить его без того, чтобы он не упал под своим собственным беспорядком. Попытка сделать его многопоточным просто сделает все, что с ним не так, намного хуже.

Вот почему я предложил переписать после того, как первые две вещи не сработали.

Mohammad Razmi

на самом деле моя проблема заключается не в дизайне пользовательского интерфейса или s.th в зависимости от пользовательского интерфейса.
результаты функции чтения будут храниться на сервере SQL Compact, и мне действительно не нужно, чтобы результат отображался в пользовательском интерфейсе.
Мне просто нужно создать программу, в которой можно было бы добавить столько читателей, сколько мне нужно, и работать отзывчиво.
это означает, что в случае добавления нового считывателя в сеть или отключения этих считывателей функция чтения других считывателей не влияет на него.именно по этой причине я должен попытаться разделить поток каждого считывателя.

honey the codewitch

Я думаю, что нашел проблему. Но я не могу это проверить. Я думаю, что ваше узкое место-это либо ваш USB-порт, либо COM-порт (в зависимости от того, что вы используете).

Я могу держать форму отзывчивой для вас, но, как вы сказали, это не имеет значения. Важно то, что вы должны иметь возможность отправлять данные в базу данных, пока происходят другие считывания карт.

Вот ваша проблема - у вас недостаточно пропускной способности. Если это USB, то вам нужно больше, чем одна шина. Если это COM - порт, вам понадобится гораздо больше из них, если вы хотите сделать много одновременных чтений.

Проходя через ваш код, действительно, запуск метода Reader() в отдельном потоке сохранит форму отзывчивой, но это не сделает аппаратное обеспечение reader более отзывчивым.

Особенно если вы используете COM-порт, там просто не хватает пропускной способности

Mohammad Razmi

там нет USB/com. Я использую маршрутизатор cisco, и мои читатели подключены к моему ноутбуку через сеть.
проблема не в пропускной способности потому что когда я открываю одну программу на ридер все они работают отлично

honey the codewitch

ладно, это справедливо. у меня возникли проблемы с отслеживанием того, где находится узкое место.

Если вызов Reader() из другого потока не решил эту проблему, и если узким местом является не то, что Вы делаете Api () (что очень похоже на чтение COM-порта), то я не знаю, где находится ваше узкое место в вашем коде, и поскольку я не могу запустить код, я не могу это выяснить.

Mohammad Razmi

Я мог бы поделиться своим рабочим столом с удаленными приложениями, и вы можете протестировать все, что захотите

honey the codewitch

Я не пойду с тобой на удаленный рабочий стол, извини. В любом случае, сейчас у меня нет на это времени.

Mohammad Razmi

Хорошо спасибо и Ваше время