Chào mừng đến với Diễn đàn lập trình - Cộng đồng lập trình.
Trang 1 của 2 12 CuốiCuối
Kết quả 1 đến 10 của 20
  1. #1
    Ngày tham gia
    Sep 2015
    Bài viết
    0

    Xử lý lỗi Cross-thread | Cross-thread operation not valid

    Khi bạn sử dụng thread, trong thread đang chạy bạn xử lý với các control và đôi lúc bạn gặp phải cái lỗi "Cross-thread operation not valid: ...". Cái lỗi này khi xài VS 2003 thì bạn không gặp phải, nhưng với > VS2005 thì chắc chắn là gặp. Và cách xử lý như sau:

    Mô tả một đoạn mã gặp lỗi:


    Mã:
    using System.Threading;     public partial class Form1 : Form    {        public Form1()        {            InitializeComponent();        }         private void AddItemN(int n)        {            for (int i = 0; i < n; i++)            {                lsbCross.Items.Add(i);            }        }         private void AddItem()        {            for (int i = 0; i < 10; i++)            {                lsbCross.Items.Add(i);            }        }         private void CrossThread()        {            AddItemN(20);        }         private void btnCross_Click(object sender, EventArgs e)        {            ThreadStart ths = new ThreadStart(AddItem);            Thread thd = new Thread(ths);            thd.Start();        }    }
    Demo có lỗi:

    http://www.ziddu.com/download.php?ui...ZlJyiYqyWlZWr2
    và đây là cái lỗi chương trình trên quẳng ra:

    Cross-thread operation not valid: Control 'lsbCross' accessed from a thread other than the thread it was created on.
    Cách xử lý thì bạn chỉ cần thay các hàm AddItem (không có tham số truyền cho hàm), AddItemN(int n) (có tham số truyền cho hàm) bằng các hàm đã xử lý cross thread như sau:


    Mã:
    #region Xử lý Cross Thread        //Xử lý cross có truyền tham số (sử dụng hàm AddItemN)        private delegate void dlgAddItemN(int n);        private void AddItemN(int n)        {            /ếu control nào đó không có properties InvokeRequired thì xài luôn this.InvokeRequired không cần phải thông qua control            if (this.lsbCross.InvokeRequired)             {                this.Invoke(new dlgAddItemN(AddItemN), n);            }            else            {                for (int i = 0; i < n; i++)                {                    this.lsbCross.Items.Add(i);                }            }        }         //Xử lý cross thread không truyền tham số cho hàm (sử dụng hàm AddItem)        private delegate void dlgAddItem();        private void AddItem()        {            if (this.lsbCross.InvokeRequired)            {                this.Invoke(new dlgAddItem(AddItem));            }            else            {                for (int i = 0; i < 10; i++)                {                    this.lsbCross.Items.Add(i);                }            }        }        #endregion
    Demo đã xử lý lỗi:

    http://www.ziddu.com/download.php?ui...hkZSnZKuamJSq7

  2. #2

    đôi lúc bạn gặp phải cái lỗi "Cross-thread operation not valid: ...". Cái lỗi này khi xài VS 2003 thì bạn không gặp phải, nhưng với > VS2005 thì chắc chắn là gặp.
    Cho nhc hỏi tại sao lại có dzụ này ? Có gì khác à ?

  3. #3
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    Trích dẫn Gửi bởi nhc1987
    Cho nhc hỏi tại sao lại có dzụ này ? Có gì khác à ?
    Không biết nữa! Nhưng lúc trước xài với máy trên trường (2003) thì không bị nhưng với máy ở nhà (2005) thì vậy [IMG]images/smilies/biggrin.png[/IMG]. Mà hỏi ông thầy ổng kêu không biết nó xử lý cái gì mà lên 2005 nó cần phải như vậy.

  4. #4
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    Đây là 1 warning ( 0 phải là lỗi ) của VS khi Debug ( nếu chạy bình thường 0 phải debug thì sẽ 0 gặp ) , nó là warning vì thỉnh thoảng nó có thể gây ra lỗi

    Nguyên nhân :

    warning này sẽ xảy ra khi bạn chạy 1 method trên 1 control từ 1 thread 0 phải là thread mà control được tạo

    cụ tỉ như dầy :
    - bạn tạo 1 control C trên thread A
    - bạn tạo 1 thread B
    - trong thread B bạn chạy 1 method của C , chẳng hạn như gọi C.Text = "" , -> warning

    warning chỉ xảy ra với những method mà có sử dụng đến handle của control C , chẳng hạn như Show , Hide , set_Text ( là thủ tục Set() của property Text ) , set_Size , ...

    Handle của control là gì :

    Nếu bạn dùng .NET mà trước kia chưa dùng Win32 và sau này cũng 0 đụng gì tới Win32 ( tức là 100% .NET đó , được dẩy thì rất quý :-D ) , bạn sẽ 0 biết về handle của control . Đại khái nó là địa chỉ của 1 window trong bộ nhớ . Class System.Windows.Forms.Control là 1 wrapper cho Window trong Win32

    Khi bạn gọi thủ tục như Control.Show , nó sẽ gọi các hàm của Win32 , như là hàm ShowWindow([Handle của Window mà class Control đang wrap ] , [... 1 vài tham số khác])

    Thấy là thủ tục Control.Show() sẽ dùng đến handle của control

    "thread mà control được tạo" là gì :

    Dim C as new Control -> đây 0 phải là lúc "control được tạo" hay đầy đủ là "handle của control chưa được tạo"

    1 control bắt đầu có handle khi nó gọi hàm Win32 CreateWindow hoặc CreateWindowEx

    Nó gọi hàm đó lúc nào , chỉ người viết ra class System.Windows.Forms.Control mới biết , hoặc bạn có thể đọc code của nó dùng .NET Reflector

    Và "thread mà control được tạo" chính là thread mà class Control gọi hàm Win32 CreateWindow

    Cách bỏ qua warning

    Đây là warning cho nên bạn có thể bỏ qua nó , như mọi warning khác . Bạn chỉ cần gọi thủ tục đây :
    System.Windows.Forms.Control.CheckForIllegalCrossT hreadCalls = False

    Cách giải quyết warning

    Bạn cần gọi các thủ tục "mà có dùng đến handle của control" trên "thread mà control được tạo"

    VD đoạn code mà sẽ gây ra warning :

    Mã:
    1. Public Class Form1
       2.  
       3.     Private Sub Form1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Click
       4.         'Tạo 1 thread mà sẽ dùng tới handle của Form1
       5.         Dim T As New Threading.Thread(AddressOf CrossThreadOperation)
       6.         T.Start()
       7.     End Sub
       8.  
       9.     Private Sub CrossThreadOperation()
      10.         'Thủ tục CrossThreadOperation có dùng tới handle của Form1
      11.         Me.Text = Date.Now.ToString 'Warning ở đây
      12.     End Sub
      13.  
      14. End Class
    Khi click dô form , warning sẽ xảy ra , để ý là bạn phải chạy Debug thì mới thấy warning

    Class Control có các method mà cho phép chạy 1 method trên thread của Control , đó là Invoke ( đồng bộ ) , BeginInvoke + EndInvoke ( 0 đồng bộ )

    Để dùng các method đó , bạn cung cấp cho nó 1 delegate tới method mà bạn muốn chạy .

    Mã:
     1. Public Class Form1
       2.  
       3.     Private Sub Form1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Click
       4.         'Tạo 1 thread mà sẽ dùng tới handle của Form1
       5.         Dim T As New Threading.Thread(AddressOf CrossThreadOperation)
       6.         T.Start()
       7.     End Sub
       8.  
       9.     Private Sub CrossThreadOperation()
      10.         'Tạo delegate
      11.         Dim MI As New MethodInvoker(AddressOf SetText)
      12.         'Chạy delegate MI trên thread của Form1
      13.         Me.Invoke(MI)
      14.     End Sub
      15.  
      16.     Private Sub SetText()
      17.         Me.Text = Date.Now.ToString
      18.     End Sub
      19.  
      20. End Class
    Khuyến mãi : Control.Invoke nó làm cái gì

    Control.Invoke(delegate) sẽ send 1 message đến "Window mà Control đang wrap" , dùng hàm Win32 SendMessage , message đó sẽ có địa chỉ của method của delegate ( tiếng anh là function pointer )

    Window nhận message trên thread của nó , qua hàm WndProc ( hàm này được windows gọi chớ 0 phải window gọi , :-D windows là hệ điều hành ) , message nằm trong tham số của WndProc

    Khi "Window mà Control đang wrap" nhận được message qua hàm WndProc , Control ( có toàn quyền xử lý hàm WndProc , WndProc chính là hàm mà class Control pass dô hàm CreateWindow ) sẽ chạy function pointer đi kèm trong message , function đó sẽ được chạy trên thread của Window

    Chấm hết , hi vọng bạn 0 lẫn lộn giữa Control , Window , Instance của Control :-D

    (Kỳ Nam - caulacbovb)

    P/s: chịu khó đọc code VB tí nhé , Để nguyên gốc tài liệu luôn [IMG]images/smilies/1.gif[/IMG]

    Một bài viết khác của sunflower: http://forums.congdongcviet.com/showthread.php?t=5752

  5. #5
    Trích dẫn Gửi bởi sunflower
    Không biết nữa! Nhưng lúc trước xài với máy trên trường (2003) thì không bị nhưng với máy ở nhà (2005) thì vậy [IMG]images/smilies/biggrin.png[/IMG]. Mà hỏi ông thầy ổng kêu không biết nó xử lý cái gì mà lên 2005 nó cần phải như vậy.
    Thầy gì lạ vậy [IMG]images/smilies/biggrin.png[/IMG]

  6. #6
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    vậy cho mình hỏi khi nào .InvokeRequired trả về giá trị True, hoặc False.
    Nếu trả về là False thì phải sử lý như thế nào vì trả về True thì hàm Invoke sau đó mới chạy.

  7. #7
    InvokeRequired mà là false thì không cần Invoke nữa. Chả cần xử lý gì cả.

  8. #8
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    ngoài ra mình còn cách nào khác nữa ko bạn, và bạn có thể thik mình hiểu thêm vì sao dùng delegate [IMG]images/smilies/smile.png[/IMG]. Thanks

  9. #9
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    Bạn ơi mình chưa hiểu lắm ở thuộc tính InvokeRequired, giải thích rõ jum mình nhe!!

  10. #10
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    đây , rất rõ, cụ thể và chi tiết :

    http://msdn.microsoft.com/en-us/libr...=VS.90%29.aspx
    [IMG]images/smilies/clap_grin.gif[/IMG]

 

 
Trang 1 của 2 12 CuốiCuối

Quyền viết bài

  • Bạn Không thể gửi Chủ đề mới
  • Bạn Không thể Gửi trả lời
  • Bạn Không thể Gửi file đính kèm
  • Bạn Không thể Sửa bài viết của mình
  •