Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Потоки. Синхронизация и семафоры. Проблема с реализацией

19K
15 февраля 2010 года
WantToProg
63 / / 19.01.2009
Доброе время суток. Извиняюсь что так написал название темы, но суть оно отражает. А проблема в следующем:
Я пишу код, который в одном потоке считывает данные из файла и передает их в последовательный порт компьютера. Второй поток возникает по событию DataReceived.
Так вот проблема возникла в связи с тем, что мне необходимо закрывать порт по окончании приема данных, и открывать его снова, при отправке. Но вероятно по причине параллельности выполнения потоков, порт не успевает закрываться. Но если проверяю проверяю с точками останова, ошибки не возникает.
Подскажите пожалуйста каким образом можно избежать данной ошибки.
Насколько я понимаю, необходимо сделать так, что бы первый поток не выполнялся пока не завершится поток по событию DataReceived, но как это сделать, я не знаю.
Проблемный код предоставляю ниже:
Код:
private void ReadSelected_Click(object sender, EventArgs e)
        {
            if (HubsCollection.CheckedItems.Count != 0)
            {              
                thread = new Thread(Do);
                thread.IsBackground = false;
                thread.Start();
            }
            else
            {
                MessageBox.Show("Выберите опрашиваемые концентраторы");
            }
        }
        private void Do()
        {
            string HubFile = @"c:\counters\data\Hubs.cts";
            string con, q, DST_ADDRESS, COUNTER_ADDRESS, COM_PORT_ADDR;
           
            StreamReader StrReader = new StreamReader(HubFile, System.Text.Encoding.Default);
            List<string> HubData = new List<string>();
            while ((con = StrReader.ReadLine()) != null)
            {
                HubData.Add(con);
            }
            for (int y = 0; y < HubData.Count(); y++)
            {                
                q = HubData[y];
                string[] Words = q.Split('!');
                string hex = "0x";
                DST_ADDRESS = Words[2];
                COM_PORT_ADDR = Words[3];
                COUNTER_ADDRESS = Words[5];
                Invoke(new ReceiveHandler(UpdateDisplay), new object[] { "Опрашиваем концентратор: " + DST_ADDRESS + " Счетчик: " + COUNTER_ADDRESS + " Порт: " + COM_PORT_ADDR});
                byte[] bytes = new byte[1024];
               
                string DST_ADD_LF = hex + DST_ADDRESS.Substring(0, 2);
                string DST_ADD_RT = hex + DST_ADDRESS.Substring(2);
                string COUNTER_ADDRESS_LF = hex + COUNTER_ADDRESS.Substring(0, 2);
                string COUNTER_ADDRESS_RT = hex + COUNTER_ADDRESS.Substring(2);
                byte D_A_L = (Convert.ToByte(DST_ADD_LF, 16));
                byte D_A_R = (Convert.ToByte(DST_ADD_RT, 16));
                byte C_A_L = (Convert.ToByte(COUNTER_ADDRESS_LF, 16));
                byte C_A_R = (Convert.ToByte(COUNTER_ADDRESS_RT, 16));
                byte[] data_conf = { 0xff, 0xff, D_A_R, D_A_L, 0x01 };                
                long CRC = CRC24(data_conf);                
                byte[] CRC_24_S = BitConverter.GetBytes(CRC);
               
                //Чтение версии прошивки концентратора
                byte[] GET_VERINFO = { CRC_24_S[0], CRC_24_S[1], CRC_24_S[2], 0xff, 0xff, D_A_R, D_A_L, 0x01, 0x83, 0x82 };
                //чтение текущего времени на коцентраторе
                byte[] GET_TIMEDATE = { CRC_24_S[0], CRC_24_S[1], CRC_24_S[2], 0xff, 0xff, D_A_R, D_A_L, 0x01, 0x81, 0x80 };
                //чтение текущей конфигурации
                byte[] GET_CONFIG = { CRC_24_S[0], CRC_24_S[1], CRC_24_S[2], 0xff, 0xff, D_A_R, D_A_L, 0x01, 0x80, 0x7f };                
                //чтение информации с почтовых ящиков
                byte[] data_rc = { 0xff, 0xff, D_A_R, D_A_L, 0x03 };
                long CRC_1 = CRC24(data_rc);
                byte[] CRC_24_RC = BitConverter.GetBytes(CRC);
                byte[] GET_MAIL = { CRC_24_RC[0], CRC_24_RC[1], CRC_24_RC[2], 0xff, 0xff, D_A_R, D_A_L, 0x03, 0x82, C_A_R, C_A_L, 0x84 };
                System.IO.Ports.SerialPort newPort = new SerialPort(COM_PORT_ADDR, 38400, Parity.None, 8, StopBits.One);
                newPort.ReadTimeout = 1000;
                               
                newPort.RtsEnable = false;
                newPort.DtrEnable = true;
                if (newPort.IsOpen == true)
                {
                    newPort.Close();
                    System.Threading.Thread.Sleep(5 * 1000);
                }
                //Открываем порт и отправляем данные
                newPort.Open();
                newPort.DataReceived += new SerialDataReceivedEventHandler(newPort_DataReceived);
                if (newPort.CtsHolding == true) { } else { };
                Send(newPort, GET_VERINFO, GET_TIMEDATE, GET_CONFIG, GET_MAIL);                
            }
        }
        private void Send(SerialPort newPort, byte[] GET_VERINFO, byte[] GET_TIMEDATE, byte[] GET_CONFIG, byte[] GET_MAIL)
        {
            newPort.Write(GET_VERINFO, 0, GET_VERINFO.Length);
            newPort.Write(GET_TIMEDATE, 0, GET_TIMEDATE.Length);
            newPort.Write(GET_CONFIG, 0, GET_CONFIG.Length);
            newPort.Write(GET_MAIL, 0, GET_MAIL.Length);
        }
        void newPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            SerialPort port = sender as SerialPort;
            Thread.Sleep(1000);
            int byteRecieved = port.BytesToRead;            
            byte[] messByte = new byte[byteRecieved];
            port.Read(messByte, 0, byteRecieved);            
            port.Close();
            string RecvMsgs = Convert.ToString(messByte.Count());
            Invoke(new ReceiveHandler(UpdateDisplay), new object[] {RecvMsgs});            
        }

PS: Причина, по которой порт необходимо закрывать по окончании приема данных, заключается в том, что в файле, который я читаю в начале порты могут быть разные (образец строки из файла 1!2380!23d8!COM3!1!0003!), а считывать и получать данные от устройств мне необходимо последовательно.
PPS: Я не программист, так что сильно не ругайте за код, просто приходится параллельно выполнять еще и такие задачи. Прислушаюсь к хорошим советам по улучшению кода.
19K
15 февраля 2010 года
Mhael
54 / / 20.03.2008
Я не вчитывался особо в код, просто совет по самой теме вопроса.
Итак у вас два потока, один читает из файла и пуляет в порт, второй.. ? Вот тут я не вижу четкого описания, вы пишите, что он просыпается по приходу данных иии..? подозреваю, что начинает читать из порта?
Сама задача их синхронизации проста, вам нужен объект синхронизации, причем даже не обязательно объект ядра, как мне кажется. Я не вижу смысла описывать тут все варианты и их особенности, есть замечательная книга Джеффри РИХТЕР "Создание эффективных WIN32-приложений с учетом специфики 64-разрядной версии Windows" . Там есть все по этой теме, разжевано и показано на примерах. Найти ее, даже бесплатно (или главы из нее, вам надо главы 8 и 9) можно в интернете без проблем.
Вам подойдет практически любой: мьютекс, семафор, крит. секция и т.п.
p.s. Если с английским все в порядке, то можно просто посмотреть мсдн, http://msdn.microsoft.com/en-us/library/ms686353%28VS.85%29.aspx .
А и еще , если вам хочется чтобы все было красиво, на классах и т.п. , то можете погуглить в сторону "живых объектов" в c++.
19K
15 февраля 2010 года
WantToProg
63 / / 19.01.2009
Цитата: Mhael
Я не вчитывался особо в код, просто совет по самой теме вопроса.
Итак у вас два потока, один читает из файла и пуляет в порт, второй.. ? Вот тут я не вижу четкого описания, вы пишите, что он просыпается по приходу данных иии..? подозреваю, что начинает читать из порта?
Сама задача их синхронизации проста, вам нужен объект синхронизации, причем даже не обязательно объект ядра, как мне кажется. Я не вижу смысла описывать тут все варианты и их особенности, есть замечательная книга Джеффри РИХТЕР "Создание эффективных WIN32-приложений с учетом специфики 64-разрядной версии Windows" . Там есть все по этой теме, разжевано и показано на примерах. Найти ее, даже бесплатно (или главы из нее, вам надо главы 8 и 9) можно в интернете без проблем.
Вам подойдет практически любой: мьютекс, семафор, крит. секция и т.п.
p.s. Если с английским все в порядке, то можно просто посмотреть мсдн, http://msdn.microsoft.com/en-us/library/ms686353%28VS.85%29.aspx .
А и еще , если вам хочется чтобы все было красиво, на классах и т.п. , то можете погуглить в сторону "живых объектов" в c++.



Все бы было отлично, но я забыл уточнить в названии. Платформа .net
Событие newPort.DataReceived += new SerialDataReceivedEventHandler(newPort_DataReceived); Возникает когда в порт поступают данные, а поступить они могут только после выполнения запроса Send(newPort, GET_VERINFO, GET_TIMEDATE, GET_CONFIG, GET_MAIL);
Таким образом я имею точно обозначенную схему обмена информацией и знаю, когда и что должно придти.
Мне посоветовали использовать EnterCriticalSection, LeaveCriticalSection, WaitForSingleObject. Но как использовать данную конструкцию в C# я не знаю

5
15 февраля 2010 года
hardcase
4.5K / / 09.08.2005
Цитата: WantToProg

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

Что мешает после того как данные отправлены получать их в том же потоке, а не обрабатывать их в событии?

19K
16 февраля 2010 года
WantToProg
63 / / 19.01.2009
Цитата: hardcase
Что мешает после того как данные отправлены получать их в том же потоке, а не обрабатывать их в событии?



А как можно событие newPort.DataReceived обработать в том же потоке?

5
16 февраля 2010 года
hardcase
4.5K / / 09.08.2005
Цитата: WantToProg
А как можно событие newPort.DataReceived обработать в том же потоке?


Зачем его обрабатывать, если есть инструменты для синхронного чтения из порта: метод Read нескольких мастей и свойство BaseStream.

19K
16 февраля 2010 года
WantToProg
63 / / 19.01.2009
Цитата: hardcase
Зачем его обрабатывать, если есть инструменты для синхронного чтения из порта: метод Read нескольких мастей и свойство BaseStream.


Есть метод Read, но как его использовать, если неизвестно получаемое количество байтов и вообще их наличие?
Если с сокетами у нас есть DataAvailable, то тут я нашел на msdn только один аналог DataReceived.
Видите в чем дело. Мне необходимо выполнить определенные процедуры:
1. Открытие порта
2. Отправка данных
3. Получение данных
4. Закрытие порта
Эта процедура должна быть выполнена для каждой строки в файле.
На что я, исходя из задачи выполняю следующее:
1. Читаю данные из файла
2. Формирую пакет на передачу
3. Открываю порт
4. Передаю данные, паралельно открывая событие newPort.DataReceived
5. По событию данные принимаются
Но в этот момент у меня происходит продолжение работы цикла и считывается следующая строка со своими параметрами, а так как в следующей строке тот же порт, то вызывается exception (порт занят).
Мне бы хотелось сделать так, чтобы пока не считаются все данные, цикл не продолжался.
Либо какую-нибудь альтернативу.
Подскажите, как можно использовать BaseStream, а то я нашел только это
PS: Я пытался использовать newPort.ReadExisting, он возвращает System.String в соотвествии с кодировкой. Мне бы просто массив байтов получить

Цитата:
Если необходимо переключиться между чтением текста и чтением двоичных данных из потока, выберите протокол, четко различающий текст и двоичные данные, например чтение байтов и декодирование данных вручную.


Как это сделать?

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог