2012-04-18 31 views
11

Tôi đã viết ứng dụng giao diện điều khiển .net 4.0 định kỳ nói chuyện với modem GSM để nhận danh sách các tin nhắn SMS đã nhận (đó là modem USB nhưng mã kết nối với nó qua cổng nối tiếp) trình điều khiển và gửi lệnh AT - tình cờ nó là một modem Sierra Wireless nhưng tôi không thể thay đổi nó và tôi có trình điều khiển mới nhất). Điều gì xảy ra là sau một khoảng thời gian (có thể là hàng giờ, có thể ngày) nó chỉ dừng hoạt động. Dưới đây là một đoạn ghi ...Lớp SerialPort thỉnh thoảng treo trên Dispose

2012-04-17 23:07:31 DEBUG Modem Check (108) - Executing AT command 'AT+CPMS="ME"'... 
2012-04-17 23:07:31 DEBUG Modem Check (108) - Finished executing 'AT+CPMS="ME"' 
2012-04-17 23:07:31 DEBUG Modem Check (108) - Detaching event handlers for 'COM13' 
2012-04-17 23:07:31 DEBUG Modem Check (108) - Disposing the SerialPort for 'COM13' 

Đó là kết thúc của các bản ghi - không có gì nhiều hơn mặc dù tôi sẽ mong đợi để xem thêm ít nhất một tuyên bố, đây là mã có liên quan:

internal T Execute() 
{ 
    var modemPort = new SerialPort(); 
    T ret; 

    try 
    { 
     modemPort.ErrorReceived += ModemPortErrorReceived; 

     modemPort.PortName = _descriptor.PortName; 
     modemPort.Handshake = Handshake.None; 
     modemPort.DataBits = 8; 
     modemPort.StopBits = StopBits.One; 
     modemPort.Parity = Parity.None; 
     modemPort.ReadTimeout = ReadTimeout; 
     modemPort.WriteTimeout = WriteTimeout; 
     modemPort.NewLine = "\r\n"; 
     modemPort.BaudRate = _descriptor.Baud; 

     if (!modemPort.IsOpen) 
     { 
      modemPort.Open(); 
     } 

     ret = _command.Execute(modemPort, _logger); 

     _logger.Debug("Detaching event handlers for '{0}'", 
         _descriptor.PortName); 

     modemPort.ErrorReceived -= ModemPortErrorReceived; 

     _logger.Debug("Disposing the SerialPort for '{0}'", 
         _descriptor.PortName); 
    } 
    catch (IOException ex) 
    { 
     _logger.Error(ex.Message); 

     throw new CommandException(
      string.Format(CultureInfo.CurrentCulture, 
          ModemWrapperStrings.COMMAND_ERROR, 
          ex.Message), 
      ex); 
    } 
    catch (UnauthorizedAccessException ex) 
    { 
     _logger.Error(ex.Message); 

     throw new CommandException(
      string.Format(CultureInfo.CurrentCulture, 
          ModemWrapperStrings.COMMAND_ERROR, 
          ex.Message), 
      ex); 
    } 
    finally 
    { 
     modemPort.Dispose(); 

     _logger.Debug("Modem on port '{0}' disposed", 
         _descriptor.PortName); 
    } 

    return ret; 
} 

Như bạn có thể thấy nó bị treo trên phương thức Vứt bỏ của lớp SerialPort.

Tôi đã làm một số Googling và tôi đã gặp phải vấn đề này: Serial Port Close Hangs the application từ chủ đề này: serial port hangs whilst closing. Các consensious có vẻ là để đóng cổng trong một chủ đề khác nhau nhưng là chỉ cho một ứng dụng hình thức? Trong trường hợp của tôi, tôi có một ứng dụng giao diện điều khiển đơn giản vì vậy tôi không nghĩ rằng nó áp dụng (nó chỉ chạy trong một vòng lặp trong chủ đề chính). Tôi thậm chí không chắc chắn nó thực sự là vấn đề này (cảm giác của tôi là nó có nhiều khả năng rằng có một vấn đề với trình điều khiển cổng nối tiếp từ modem nhưng tôi không biết và có lẽ tôi đang không công bằng với modem). Theo như tôi nhìn thấy nó tôi có ba lựa chọn:

  1. Đóng cảng trong một thread khác
  2. Đặt trong một sự chậm trễ trước khi chốt cổng
  3. Rời khỏi cổng mở mãi mãi

tôi không thực sự thích bất kỳ giải pháp nào nhưng tôi đang nghĩ đến việc mở cổng và chỉ nhìn thấy những gì xảy ra (tôi có cảm giác rằng nó sẽ rò rỉ bộ nhớ hoặc tệ hơn, phơi bày một số vấn đề khác với modem nhưng có lẽ tôi chỉ bi quan và nếu đó là trường hợp tôi có thể có thể lấy đi với đóng nó mỗi 24 giờ, s ay, và mở lại nó một lần nữa) vì vậy câu hỏi của tôi là ...

Có vấn đề gì khác với mã này có thể gây ra sự bevahior này hoặc có cách giải quyết thay thế cho những gì tôi đã nêu ở trên không?

+0

gì đang xảy ra bên trong _command.Execute (..)? – PeskyGnat

+0

Bạn đã thử sử dụng giải pháp thay thế được đề xuất chưa? Tôi hiểu tất cả các ví dụ là Winforms, nhưng [bài viết mẹo] (http://blogs.msdn.com/b/bclteam/archive/2006/10/10/top-5-serialport-tips-_5b00_kim-hamilton_5d00_. aspx) mô tả vấn đề rất rõ ràng. Nó sẽ có giá trị ít nhất là cho nó một shot. –

+0

Nó chỉ là gửi lệnh và nhận được một phản hồi trở lại (trong trường hợp này nó được gửi AT + CPMS = "ME") - nó đi ra khỏi cuộc gọi tốt mặc dù như tôi đã nhận "Xử lý SerialPort cho" thông điệp tường trình vì vậy tôi figured nó không quá liên quan đến những gì nó thực sự đã làm trong đó? Có điều gì mà tôi có thể làm mà có thể dẫn đến việc bị treo không? – kmp

Trả lời

12

SerialPort có phần dễ bị bế tắc. Cho đến nay nguyên nhân phổ biến nhất là nguyên nhân bạn tìm thấy, nó được kích hoạt bằng cách sử dụng Invoke() trong trình xử lý sự kiện DataReceived. Rõ ràng không phải trường hợp của bạn ở đây.

Những bế tắc này có liên quan đến một chuỗi công nhân mà SerialPort bắt đầu phía sau bức màn. Chuỗi đó giúp phát hiện các sự kiện không đồng bộ trên cổng, winapi gốc cơ bản là WaitCommEvent(). Công nhân đó làm cho các sự kiện DataReceived, PinChanged và ErrorReceived hoạt động. Lưu ý cách bạn làm sử dụng ErrorReceived.

Phương thức Dispose() thực hiện tương tự như phương thức Close(), nó báo hiệu chuỗi công nhân thoát ra. Tuy nhiên, lỗ hổng là nó không đợi cho chuỗi thoát. Đó là một công thức gây rắc rối, loại được viết rõ ràng trong bài viết MSDN cho SerialPort.Close() trong phần Ghi chú:

Cách tốt nhất cho bất kỳ ứng dụng nào là chờ một khoảng thời gian sau khi gọi Đóng phương thức trước khi thử gọi phương thức Open, vì cổng có thể không được đóng ngay lập tức.

Thực tế là điều tồi tệ nhất có thể cho lời khuyên "thực hành tốt nhất" vì nó không chỉ định chính xác thời gian bạn chờ đợi. Vì một lý do chính đáng, không có giá trị an toàn được đảm bảo. Chờ đợi một hoặc hai giây nên được 99,9% tốt. Chế độ thất bại 0,1% xảy ra khi máy được tải nặng và chuỗi công nhân chỉ đơn giản là không có đủ chu kỳ để phát hiện tình trạng gần đúng lúc. Hoàn toàn không thể tha thứ tất nhiên.

Làm nổi bật vấn đề này, chỉ bao giờ mở cổng nối tiếp ở đầu chương trình của bạn và đóng nó khi thoát. Ngắn từ rắc rối luồng, điều này cũng đảm bảo rằng bạn không ngẫu nhiên mất quyền truy cập vào cổng khi một chương trình khác nhảy vào và lấy cắp cổng cách xa bạn. Và lưu ý rằng việc đóng cổng không thực sự cần thiết nữa, Windows sẽ xử lý nó nếu bạn không làm vậy.

+0

Cảm ơn, thông tin tốt - Tôi sẽ đi với tùy chọn 3 sau đó và xem những gì sẽ xảy ra! Ngẫu nhiên, nếu tôi chỉ không bận tâm với sự kiện ErrorReceived (điều đó có nghĩa là thread nền này sẽ không được bắt đầu ở tất cả)? Và nó được treo trên Vứt bỏ, không phải là mở vì vậy tôi tìm thấy những điều chậm trễ sẽ không áp dụng nó? – kmp

1

Nếu bạn đang sử dụng sự kiện DataRecieved hoặc bất kỳ sự kiện nào khác từ đối tượng cổng nối tiếp, bạn nên xóa (các) trình xử lý sự kiện trước khi xử lý cổng nối tiếp.

mySerial.DataReceived -= DataReceivedHandler; 
mySerial.Dispose(); 

Treo xảy ra vì bạn có sự kiện kích hoạt trên vật thể đã bị xử lý ... rõ ràng là lỗi.

Tuy nhiên, trong trường hợp của bạn, bạn đã thực hiện điều đó .. và sự cố xảy ra vì cổng chưa đóng. có thể một thread.sleep có thể cho phép cổng để "giải quyết" trước khi bạn cố gắng mở lại nó. Nó có lẽ là phần cứng cụ thể là tốt ... đó là lý do tại sao không có thực hành tốt nhất.

Giống như một hình thức kiểm soát: How to remove all event handlers from a control

Các vấn đề liên quan