2008-09-29 20 views
7

Tôi có một ứng dụng winforms, vấn đề phải làm với luồng. Vì tôi đang gọi 'MyCustomCode() tạo chuỗi mới và gọi phương thức ' SomeMethod() ', sau đó truy cập MessageBox.Show (...).Vấn đề luồng Winforms, chuỗi thứ hai không thể truy cập các điều khiển biểu mẫu chính thứ nhất

Sự cố phải làm với luồng, vì chuỗi mới được tạo đang cố truy cập điều khiển được tạo trên một chuỗi khác.

Tôi nhận được lỗi:

Cross-chủ đề hoạt động không hợp lệ: Control 'TestForm' truy cập từ một thread khác với thread nó đã được tạo ra về.

public TestForm() 
{ 
    InitializeComponent(); 


    // custom code 
    // 
    MyCustomCode(); 


} 

public void SomeMethod() 
{ 

    // ***** This causes an error **** 

    MessageBox.Show(this, 
     ex.Message, 
     "Error", 
     MessageBoxButtons.OK, 
     MessageBoxIcon.Error 
    ); 
} 



private void InitializeAutoUpdater() 
{ 
     // Seperate thread is spun to keep polling for updates 
     ThreadStart ts = new ThreadStart(SomeMethod); 
     pollThread = new Thread(ts); 
     pollThread.Start(); 
} 

Cập nhật

Nếu bạn nhìn vào ví dụ này http://www.codeproject.com/KB/cs/vanillaupdaterblock.aspx, phương pháp CheckAndUpdate đang kêu gọi MessageBox.Show (..) mà là vấn đề của tôi là gì. Tôi đã nghĩ rằng mã đó là tốt để đi!

Điều thú vị là mã này có hoạt động tốt vào thứ Sáu không ???

+0

có thể là do tôi đã cài đặt .net 3.5? Đây có phải là tính năng 3.5 'không? Tôi nghi ngờ nó nhưng nó là lời giải thích duy nhất! –

+0

(Gần đây tôi đã cài đặt nó ..) –

Trả lời

9

Bạn không thể truy cập các phần tử giao diện người dùng từ nhiều chuỗi.

Một cách để giải quyết điều này là gọi phương thức Gọi của điều khiển với đại biểu đến hàm sử dụng các phần tử giao diện người dùng (như hộp thông báo). Somethin như:

public delegate void InvokeDelegate(); 

public void SomeMethod() 
{ 

    button1.Invoke((InvokeDelegate)doUIStuff); 


} 


void doUIStuff() 
{ 
      MessageBox.Show(this, 
       ex.Message, 
       "Error", 
       MessageBoxButtons.OK, 
       MessageBoxIcon.Error 
      ); 
} 
+0

Bạn nên kiểm tra xem có yêu cầu gọi hay không, ví dụ: button1.InvokeRequired. – RickL

+0

Và nếu xử lý của biểu mẫu chưa được tạo, InvokeRequired luôn trả về false. Đó là lý do tại sao SynchronizationContext có thể được khuyến khích nhiều hơn. –

0

Bạn nên KHÔNG sử dụng BeginInvoke, bạn nên sử dụng Invoke, sau đó khi bạn hiểu rằng, bạn có thể nhìn vào sử dụng BeginInvoke nếu thực sự cần thiết.

0
'******************************************************************* 
' Get a new processor and fire it off on a new thread. 
'******************************************************************* 
fpProc = New Processor(confTable, paramFile, keyCount) 
AddHandler fpProc.LogEntry, AddressOf LogEntry_Handler 
Dim myThread As System.Threading.Thread = New System.Threading.Thread(AddressOf fpProc.ProcessEntry) 
myThread.Start() 

Sau đó, trong ứng dụng cha mẹ bạn có:

'************************************************************************* 
'  Sub: LogEntry_Handler() 
' Author: Ron Savage 
' Date: 08/29/2007 
' 
' This routine handles the LogEntry events raised by the Processor class 
' running in a thread. 
'************************************************************************* 
Private Sub LogEntry_Handler(ByVal logLevel As Integer, ByVal logMsg As String) Handles fProc.LogEntry 
writeLogMessage(logMsg); 
End Sub 

Đó là những gì tôi làm.

+2

WTF? Cái gì thế này? – leppie

+0

Nó sử dụng hàng đợi thông báo sự kiện để xử lý thông tin liên lạc quá trình (thread cho phụ huynh trong trường hợp này. :-) Tôi có một "số chưa biết" của tất cả các chủ đề gửi cập nhật cho cùng một cửa sổ cha mẹ. –

+0

Tôi đồng ý với leppie. – RickL

7

để tránh trường hợp ngoại lệ cross-ren (InvalidOperationException), đây là mẫu mã:

protected delegate void someGuiFunctionDelegate(int iParam); 

protected void someGuiFunction(int iParam) 
{ 
    if (this.InvokeRequired) 
    { 
     someGuiFunctionDelegate dlg = new 
      someGuiFunctionDelegate(this.someGuiFunction); 
     this.Invoke(dlg, new object[] { iParam }); 
     return; 
    } 

    //do something with the GUI control here 
} 

tôi đồng ý rằng đây là khó chịu, nhưng nó là một artifact của thực tế là cửa sổ giao diện điều khiển được không thread- an toàn. Ngoại lệ có thể được tắt với một lá cờ ở đâu đó hoặc khác, nhưng không làm điều đó vì nó có thể dẫn đến cực kỳ khó tìm lỗi.

1

Quy tắc số một khi đa luồng là bạn hoàn toàn không thể chạm vào giao diện người dùng từ chuỗi công việc. Có rất nhiều cách để thực hiện đa luồng và rất khó để có được nó "đúng".

Dưới đây là một bài báo ngắn gọn rằng sẽ giúp bạn ra - Updating the UI from a Secondary Thread

Và đây là một bài viết dài mà thảo luận về luồng sâu - Multi-threading in .NET

3

Để giữ cho mọi thứ đơn giản, bạn có thể nhìn vào cách sử dụng các lớp BackGroundWorker. Lớp này sẽ cung cấp một khung để xử lý các sự kiện thông báo luồng và tiến trình của bạn. Chuỗi ui của bạn sẽ xử lý sự kiện tiến trình và hiển thị thông báo lỗi mà bạn trả về.

0

Kiểm tra InvokeRequired

0

Tôi thực sự giống như một cuộc gọi đệ quy.

public delegate void InvokeDelegate(string errMessage); 

    public void SomeMethod() 
    { 
     doUIStuff("my error message"); 
    } 


    void doUIStuff(string errMessage) 
    { 
     if (button1.InvokeRequired) 
      button1.Invoke((InvokeDelegate)doUIStuff(errMessage)); 
     else 
     { 
       MessageBox.Show(this,  
        ex.Message, 
        errMessage, 
        MessageBoxButtons.OK, 
        MessageBoxIcon.Error 
       ); 
     } 
    } 
1

Tôi biết đây là bài đăng cũ hơn, nhưng gần đây tôi đã tìm thấy giải pháp thanh lịch cho vấn đề này bằng cách sử dụng các phương pháp mở rộng và generics. Đây là sự kết hợp giữa các tác giả và một số ý kiến.

một phương pháp chung cho Winforms Cross-chủ đề truy cập

http://www.codeproject.com/KB/cs/GenericCrossThread.aspx

public static void Manipulate<T>(this T control, Action<T> action) where T : Control 
{ 
    if (control.InvokeRequired) 
    { 
     control.Invoke(new Action<T, Action<T>>(Manipulate), 
        new object[] { control, action }); 
    } 
    else 
    { action(control); } 
} 

này có thể được gọi theo cách sau đây, vì đơn giản tôi đã sử dụng một nhãn.

someLabel.Manipulate(lbl => lbl.Text = "Something"); 
Các vấn đề liên quan