2011-09-23 30 views
7

Tôi đã tạo một Ứng dụng WPF đơn giản và thêm một nút vào cửa sổ mặc định. Khi tôi nhấp vào nút, một phương pháp làm việc dài mô phỏng (được mô phỏng bằng cách sử dụng Thread.Sleep (15000) được gọi .Tôi đang cố gắng làm cho nút thực thi không đồng bộ, mặc dù sau các ví dụ trực tuyến, nút và toàn bộ cửa sổ khóa ngay khi tôi bấm và vẫn như vậy cho đến khi Thread.Sleep (...) kết thúcUI Thread Block

Bất cứ ý tưởng tại sao điều này đang xảy ra

đây là mã:.?

private void button1_Click(object sender, RoutedEventArgs e) 
{ 
    DoSomeAsyncWork(); 
} 

private void DoSomeAsyncWork() 
{ 
    System.Windows.Threading.Dispatcher.Run(); 
    Thread thread = new System.Threading.Thread(
     new System.Threading.ThreadStart(
      delegate() 
      { 
       Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => Thread.Sleep(15000))); 
      } 
     )); 
    thread.Start(); 
} 

Trả lời

12

Bạn đang đặt hoạt động dài trở lại vào chuỗi giao diện người dùng. Hãy để tôi bình luận ví dụ của bạn:

Thread thread = new System.Threading.Thread( 
    new System.Threading.ThreadStart( 
     delegate() { 
      // here we are in the background thread 

      Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, 
       new Action(() => { 
        // here we are back in the UI thread 
        Thread.Sleep(15000); 
       })); 
     } 
    )); 

Vì vậy, bạn nên thay đổi ví dụ của bạn như thế này:

Thread thread = new System.Threading.Thread( 
    new System.Threading.ThreadStart( 
     delegate() { 
      // here we are in the background thread 

      Thread.Sleep(15000); // <-- do the long operation here 

      Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, 
       new Action(() => { 
        // here we are back in the UI thread 

        // do stuff here that needs to update the UI after the operation finished 
       })); 
     } 
    )); 

Như những người khác đã đề cập, nó dễ dàng hơn để sử dụng lớp BackgroundWorker. Dưới đây là ví dụ:

private void DoSomeAsyncWork() 
{ 
    BackgroundWorker bw = new BackgroundWorker(); 

    bw.DoWork += (sender, args) => { 
     // do your lengthy stuff here -- this will happen in a separate thread 
     Thread.Sleep(15000); 
    } 

    bw.RunWorkerCompleted += (sender, args) => { 
     if (args.Error != null) // if an exception occurred during DoWork, 
      MessageBox.Show(args.Error.ToString()); // do your error handling here 

     // do any UI stuff after the long operation here 
     ... 
    } 

    bw.RunWorkerAsync(); // start the background worker 
} 
3

Bằng cách sử dụng BeginInvoke bạn đang thực sự thực hiện các mã trên thread UI.

Bạn chỉ cần sử dụng điều này khi bạn đang cập nhật giao diện người dùng của mình từ chuỗi công nhân nền. Nếu bạn chỉ cần có:

Thread thread = new System.Threading.Thread(
    new System.Threading.ThreadStart(
     delegate() 
     { 
      Thread.Sleep(15000); 
     } 
    )); 

Tôi nghĩ nó sẽ hoạt động.

Tuy nhiên, bạn không nâng cao sự kiện "Đã hoàn thành công việc" để bạn không có cách nào biết khi nào (hoặc thực sự nếu) chuỗi đã kết thúc. Nhìn vào BackgroundWorker class. Điều này làm rất nhiều việc nâng hạng nặng cho bạn. Bạn chỉ cần cắm mã của mình vào phương thức DoWork.

+0

Tôi thực sự đang cố gắng thực thi một số mã yêu cầu chuỗi riêng và khi tôi đặt dòng mã này trực tiếp trong đại biểu đầu tiên (tức là bạn có Thread.Sleep (15000)) I nhận được thông báo cho biết rằng chuỗi không thể truy cập các đối tượng. Đây là thông báo: "Chuỗi cuộc gọi không thể truy cập đối tượng này bởi vì một chuỗi khác sở hữu nó." Rõ ràng, đó là một vấn đề phổ biến nhưng làm thế nào tôi đang đấu tranh để có được xung quanh này mà không đi xuống con đường của việc sử dụng một nhân viên nền. Vì vậy, khi tôi thêm vào lồng nhau BeginInvoke thứ hai gọi đối tượng được tìm thấy nhưng giao diện người dùng bị chặn. – fin

+0

Vì vậy, tôi đoán, những gì tôi đang tìm kiếm là một phương pháp để đặt logic của tôi dường như yêu cầu truy cập vào chuỗi giao diện người dùng trên một chuỗi nền mới. Để tóm tắt, cấp quyền truy cập cho các đối tượng UI nhưng chạy trên một chuỗi riêng biệt trong nền? – fin

+0

@Finbar: Bạn không thể "cấp quyền truy cập". Hoạt động UI * phải * được thực hiện trong chuỗi giao diện người dùng. Tuy nhiên, những gì bạn có thể làm là chia các hoạt động giao diện người dùng thành các bit nhỏ, đặt vòng lặp lớn trong chuỗi nền và gọi 'Dispatcher.BeginInvoke (... hoạt động giao diện người dùng ngắn ...)' bên trong vòng lặp. Điều này làm cho giao diện người dùng bị chặn ngay nhiều lần thay vì một lần trong một thời gian dài. – Heinzi

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