2012-12-25 3 views
4

Я пытаюсь сделать модуль связи запрос-ответ в C#, используя SerialPort. Это очень простая реализация, просто чтобы продемонстрировать, что она работает (SerialPort не работает должным образом (это виртуальный COM-порт USB) и иногда ест несколько символов, возможно, некоторые ошибки драйвера Windows).Ошибка блокировки в C#

Однако демо не работает: -/

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

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

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

Мой код:

SerialPort sp; 

    public byte[] SendCommand(byte[] command) 
     { 
      //System.Threading.Thread.Sleep(100); 
      lock (sp) 
      { 
       Console.Out.WriteLine("ENTER"); 
       try 
       { 

        string base64 = Convert.ToBase64String(command); 

        string request = String.Format("{0}{1}\r", target_UID, base64); 

        Console.Out.Write("Sending request... {0}", request); 

        sp.Write(request); 

        string response; 

        do 
        { 
         response = sp.ReadLine(); 
        } while (response.Contains("QQ==")); 

        Console.Out.Write("Response is: {0}", response); 

        return Convert.FromBase64String(response.Substring(target_UID.Length)); 
       } 

       catch (Exception e) 
       { 
        Console.WriteLine("ERROR!"); 
        throw e; 
       } 
       finally 
       { 
        Console.Out.WriteLine("EXIT"); 
       } 
      } 

     } 

Выход:

ENTER 
Sending request... C02UgAABAA= 
Response is: cgAABAAARwAAAA== 

EXIT 
ENTER 
Sending request... C02UgQARwA= 
ENTER 
Sending request... C02UgAABAA= 
Response is: gQARwAAPHhtbD48bWVzc2FnZT5IZWxsbyBYWDIhPC9tZXNzYWdlPjxkZXN0aW5haXRvbj5NaXNpPC9kZXN0aW5hdGlvbj48L3htbD4= 

Обратите внимание на два ENTER-s, без выхода между ними? Как это возможно?

+0

Я не уверен, что это устраняет эту проблему, но это как правило, гораздо безопаснее, чтобы зафиксировать на частный объект, не связанный с объектом зра (например, 'частный шкафчиком объекта;'), чтобы избежать любого возможного внутреннего использования блокировки на (в данном случае) объекта 'sp'. – Joe

+0

Изменено, но ситуация не меняется. Спасибо за совет! –

ответ

5

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

Следующая деталь, которую вы должны знать, это то, что пользовательский интерфейс является специальным, это повторный участник. Вызов sp.ReadLine(); блокирует поток пользовательского интерфейса. Это незаконно, поток пользовательского интерфейса программы GUI работает как «однопоточная квартира», включенная атрибутом [STAThread] в методе Main() вашей программы.Контракт потока STA запрещает его блокировать, что очень вероятно вызывает тупик.

Чтобы выполнить требования STA, CLR делает что-то особенное, если код, выполняющийся в потоке пользовательского интерфейса, выполняет операцию блокировки, например SerialPort.ReadLine(). Он накачивает контур сообщения, чтобы сообщения, отправляемые Windows, отправлялись на отправку. Этот цикл сообщений делает то же самое, что и Application.Run().

Возможно, вы можете видеть, где это происходит, свойство PropertyGrid разрешено снова вызвать метод SendCommand(). Блокировка не работает вообще, это происходит в той же теме.

Решение этой проблемы не так просто, мы не видим код, который вызывает SendMessage(). Но вам нужно будет как-то предотвратить это. Больше информации об этом поведении в this question.

+0

Очень интересно, спасибо за ваш подробный ответ. –

+0

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

+0

Но если вы говорите правду (что в случае блокировки потока пользовательского интерфейса очередь сообщений накачивается), как можно использовать свойствоGrids на объекты, которые должны выполнять сетевые передачи (в нашем случае SerialPort является «сетью»), чтобы рассчитать их свойства? –

5

Где находится поле sp? Замки работают только с ненулевыми объектами.

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

private static readonly object _lockObject = new object(); 

Edit: теперь я вижу, основываясь на комментариях в других ответах, что вы на самом деле работаете эту логику в потоке пользовательского интерфейса , что заставляет блокировку повторно вводить несколько раз в тот же поток (поток пользовательского интерфейса), когда очередь сообщений накачивается. Запустите этот код в другом потоке, и вы получите два преимущества: (1) пользовательский интерфейс не будет блокироваться, так как этот потенциально длительный код выполняется, и (2) блокировка всегда будет получена в новом потоке, гарантируя, что последующие вызовы SendCommand будут в отдельном потоке и, таким образом, будут последовательно вводить блокировку по желанию.

+0

SP - это (частный) объект класса, и он назначается в конструкторе –

+0

. Также был использован отдельный частный, статический объект readonly, но ситуация такая же. –

+0

Не могли бы вы предоставить больше контекста, например, когда вызывается SendCommand и как запускается несколько потоков? – jam40jeff

0

Есть две вещи, которые вы должны попробовать/изменение:

1.Make отдельное поле, которое будет использоваться для блокировки только

2.Apply двойной проверки замка: double check locking

+0

Пробовал первый, ничего не меняется. –

+0

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

0

SerialPort sp;

public byte[] SendCommand(byte[] command) 
    { 
     //System.Threading.Thread.Sleep(100); 
     lock (sp) 
     { 
      Console.Out.WriteLine("ENTER"); 
      try 
      { 

       string base64 = Convert.ToBase64String(command); 

       string request = String.Format("{0}{1}\r", target_UID, base64); 

       Console.Out.Write("Sending request... {0}", request); 

       sp.Write(request); 

       string response; 

       do 
       { 
        response = sp.ReadLine(); 
       } while (response.Contains("QQ==")); 

       Console.Out.Write("Response is: {0}", response); 

       return Convert.FromBase64String(response.Substring(target_UID.Length)); 
      } 

      catch (Exception e) 
      { 
       Console.WriteLine("ERROR!"); 
       throw e; 
      } 
      finally 
      { 
       Console.Out.WriteLine("EXIT"); 
      } 
     } 

    } 
Смежные вопросы