2009-06-23 32 views
15
public void MyTest() 
{ 
    bool eventFinished = false; 

    myEventRaiser.OnEvent += delegate { doStuff(); eventFinished = true; }; 
    myEventRaiser.RaiseEventInSeperateThread() 

    while(!eventFinished) Thread.Sleep(1); 

    Assert.That(stuff); 
} 

Tại sao sự kiện không thểĐược hoàn thành dễ bay hơi và có quan trọng không? Dường như với tôi rằng trong trường hợp này trình biên dịch hoặc thời gian chạy có thể trở nên thông minh vì lợi ích của chính nó và 'biết' trong vòng lặp while eventFinished chỉ có thể là sai. Đặc biệt là khi bạn xem xét cách một biến được tạo ra như là một thành viên của một lớp và ủy nhiệm như là một phương thức của cùng một lớp và do đó không được tối ưu hóa sự kiện eventFinished đã từng là một biến cục bộ.tại sao không thể biến cục bộ dễ bay hơi trong C#?

+2

Biến của bạn không phải là địa phương trong trường hợp này! Thay vào đó, nó là một biến cá thể trong một lớp do trình biên dịch tạo ra. –

+4

Đó là một sự khác biệt ngữ nghĩa - về mặt mã, nó là một biến cục bộ xảy ra để bị bắt ... –

+1

Cái tôi thấy 'là một biến cục bộ được cập nhật trong một luồng khác. Và mặc dù tôi biết trình biên dịch sẽ tạo ra một biến cá thể của biến cục bộ của tôi, trình biên dịch tiền trước dường như không hoặc là 'quá bướng bỉnh' để thừa nhận nó. –

Trả lời

12

Có tồn tại một nguyên thủy luồng, ManualResetEvent để thực hiện chính xác tác vụ này - bạn không muốn sử dụng cờ boolean.

Something như thế này nên thực hiện công việc:

public void MyTest() 
{ 
    var doneEvent = new ManualResetEvent(false); 

    myEventRaiser.OnEvent += delegate { doStuff(); doneEvent.Set(); }; 
    myEventRaiser.RaiseEventInSeparateThread(); 
    doneEvent.WaitOne(); 

    Assert.That(stuff); 
} 

Về thiếu sự hỗ trợ cho các từ khóa volatile trên các biến địa phương, tôi không tin rằng có bất kỳ lý do tại sao điều này có thể không về mặt lý thuyết có thể bằng C#. Rất có thể, nó không được hỗ trợ đơn giản chỉ vì không có sử dụng cho một tính năng như vậy trước C# 2.0. Bây giờ, với sự tồn tại của các phương thức nặc danh và các hàm lambda, sự hỗ trợ đó có thể trở nên hữu ích. Ai đó xin vui lòng làm rõ vấn đề nếu tôi đang thiếu một cái gì đó ở đây.

+2

Eric ở đâu khi bạn cần anh ấy? ;-p –

+0

Hehe. Thật vậy, chúng tôi đang đi vào những khu vực u ám của C#/CLR ngay bây giờ - mà tôi chắc chắn rằng anh ấy có thể làm sáng tỏ. – Noldorin

+0

Cảm ơn bạn đã sử dụng ManualResetEvent. Nó thậm chí hoạt động khi sự kiện xảy ra được gọi trong cùng một luồng, đó là một khả năng mà tôi không muốn loại trừ trong MyTest(). –

1

Điều gì sẽ xảy ra nếu Sự kiện được nêu không hoàn thành cho đến sau khi quá trình đã thoát khỏi phạm vi của biến cục bộ đó? Biến này sẽ được giải phóng và luồng của bạn sẽ thất bại.

Cách tiếp cận hợp lý là đính kèm chức năng đại biểu chỉ ra cho chuỗi chủ đề mà chuỗi phụ đã hoàn thành.

+0

Mã là tốt; đó là một biến "được capture", và được thực hiện như một trường trên một lớp do trình biên dịch tạo ra. Chủ đề sẽ không thành công. –

+0

Dù sao thì nó cũng không giống với một biến cấp lớp? Nếu cá thể được thu thập rác trước khi sự kiện trong luồng khác kết thúc, thì vấn đề tương tự sẽ xảy ra. – Noldorin

+0

Nó không thể được thu thập rác trong khi nó vẫn còn trong phạm vi nhìn thấy được của một trong hai chủ đề. –

10

Trong các trường hợp nhất, biến cục bộ cụ thể cho một chuỗi, do đó các vấn đề liên quan đến volatile là hoàn toàn không cần thiết.

Điều này thay đổi khi nào, như trong ví dụ của bạn, nó là một biến "bị bắt" - khi nó được thực hiện âm thầm như một trường trên một lớp do trình biên dịch tạo ra. Vì vậy, theo lý thuyết nó có thể là dễ bay hơi, nhưng trong hầu hết các trường hợp nó sẽ không có giá trị thêm phức tạp.

Cụ thể, một cái gì đó giống như Monitor (còn gọi là lock) với Pulse v.v. cũng có thể làm điều này cũng như bất kỳ số lượng cấu trúc luồng khác.

Threading là khôn lanh, và một vòng lặp hoạt động hiếm khi cách tốt nhất để quản lý nó ...


Re chỉnh sửa ... secondThread.Join() sẽ là điều hiển nhiên - nhưng nếu bạn thực sự muốn sử dụng một mã thông báo riêng, xem bên dưới. Lợi thế của điều này (trên những thứ như ManualResetEvent) là nó không yêu cầu bất cứ điều gì từ hệ điều hành - nó được xử lý hoàn toàn bên trong CLI.

using System; 
using System.Threading; 
static class Program { 
    static void WriteLine(string message) { 
     Console.WriteLine(Thread.CurrentThread.Name + ": " + message); 
    } 
    static void Main() { 
     Thread.CurrentThread.Name = "Main"; 
     object syncLock = new object(); 
     Thread thread = new Thread(DoStuff); 
     thread.Name = "DoStuff"; 
     lock (syncLock) { 
      WriteLine("starting second thread"); 
      thread.Start(syncLock); 
      Monitor.Wait(syncLock); 
     } 
     WriteLine("exiting"); 
    } 
    static void DoStuff(object lockHandle) { 
     WriteLine("entered"); 

     for (int i = 0; i < 10; i++) { 
      Thread.Sleep(500); 
      WriteLine("working..."); 
     } 
     lock (lockHandle) { 
      Monitor.Pulse(lockHandle); 
     } 
     WriteLine("exiting"); 
    } 
} 
+0

Điều gì xảy ra nếu RaiseEventInSeperateThread() được triển khai hiệu quả như: Thread mới (() => {Thread.sleep (100); OnEvent();}; Làm thế nào bạn sử dụng Màn hình hoặc khóa để có MyTest() –

+0

Tôi có nghĩa là: chủ đề mới (() => {Thread.sleep (100); OnEvent();}). Bắt đầu(); –

+0

Bạn sẽ có một chủ đề WaitOne và Pulse khác. Tôi sẽ cố gắng thêm một ví dụ sau ... –

4

Bạn cũng có thể sử dụng Voltile.Write nếu bạn muốn làm cho biến cục bộ hoạt động như dễ bay hơi. Như trong:

public void MyTest() 
{ 
    bool eventFinished = false; 

    myEventRaiser.OnEvent += delegate { doStuff(); Volatile.Write(ref eventFinished, true); }; 
    myEventRaiser.RaiseEventInSeperateThread() 

    while(!Volatile.Read(eventFinished)) Thread.Sleep(1); 

    Assert.That(stuff); 
} 
+1

câu trả lời hay nhưng vòng lặp 'while' của bạn nên sử dụng' Volatile.Read' – CoderBrien

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