2012-10-05 41 views
36

Sau khi yêu cầu this question, tôi tự hỏi liệu có thể chờ một sự kiện được kích hoạt hay không và sau đó lấy dữ liệu sự kiện và trả lại một phần. Sắp xếp như thế này:Làm thế nào để chặn cho đến khi một sự kiện được kích hoạt trong C#

private event MyEventHandler event; 
public string ReadLine(){ return event.waitForValue().Message; } 
... 
event("My String"); 
...elsewhere... 
var resp = ReadLine(); 

Hãy đảm bảo mọi giải pháp bạn cung cấp đều trả về giá trị trực tiếp thay vì lấy từ giá trị khác. Tôi hỏi liệu phương pháp trên có sẵn theo một cách nào đó không. Tôi biết về Auto/ManuelResetEvent, nhưng tôi không biết rằng họ trả về giá trị trực tiếp như tôi đã làm ở trên.

Cập nhật: Tôi đã khai báo sự kiện bằng cách sử dụng MyEventHandler (có chứa trường Message). Tôi có một phương pháp trong một chủ đề khác gọi là ReadLine đang chờ sự kiện kích hoạt. Khi sự kiện kích hoạt phương thức WaitForValue (một phần của cảnh xử lý sự kiện) trả về sự kiện args, chứa thông báo. Thông điệp này sau đó được trả về bởi ReadLine cho bất cứ điều gì đã gọi nó.

The accepted answer to that question Tôi hỏi là những gì tôi đã làm, nhưng nó không cảm thấy hoàn toàn đúng. Nó gần như cảm thấy như một cái gì đó có thể xảy ra với dữ liệu giữa việc bắn ManuelResetEvent và chương trình lấy dữ liệu và trả lại dữ liệu đó.

Cập nhật: Vấn đề chính với Auto/ManualResetEvent là sự cố quá dễ bị tổn thương. Một chủ đề có thể chờ đợi sự kiện, và sau đó không cung cấp đủ thời gian cho bất cứ ai khác để có được nó trước khi thay đổi nó để cái gì khác. Có cách nào để sử dụng ổ khóa hay cái gì khác không? Có thể sử dụng get và set statement.

+0

những gì về một vòng lặp while: 'while (someGlobalvar);' cahnge 'someGlobalvar' trong một chức năng bạn asign đến sự kiện này. – elyashiv

+9

nó đang hoạt động chờ đợi, và nó là một giải pháp rất xấu –

+0

Bây giờ, 2 năm sau, có vẻ như tôi chỉ nên làm WaitTwo mà sẽ trả về một đối tượng, và '* ResetEvent.Set (dữ liệu đối tượng)'. Tôi vẫn sẽ cần phải lo lắng về tham chiếu đối tượng, nhưng đó là bình thường. Ít nhất con trỏ sẽ không thay đổi. –

Trả lời

25

Bạn có thể sử dụng ManualResetEvent. Đặt lại sự kiện trước khi bạn kích hoạt chuỗi thứ cấp và sau đó sử dụng phương thức WaitOne() để chặn luồng hiện tại. Sau đó, bạn có thể đặt chuỗi thứ hai là ManualResetEvent sẽ làm cho luồng chính tiếp tục. Một cái gì đó như thế này:

ManualResetEvent oSignalEvent = new ManualResetEvent(false); 

void SecondThread(){ 
    //DoStuff 
    oSignalEvent.Set(); 
} 

void Main(){ 
    //DoStuff 
    //Call second thread 
    System.Threading.Thread oSecondThread = new System.Threading.Thread(SecondThread); 
    oSecondThread.Start(); 

    oSignalEvent.WaitOne(); //This thread will block here until the reset event is sent. 
    oSignalEvent.Reset(); 
    //Do more stuff 
} 
+0

Điều này không trả lại giá trị trực tiếp. Tôi đã thử điều đó trong câu hỏi khác. Không có bất cứ điều gì như tôi hỏi về? –

2

Một loại rất dễ dàng của sự kiện bạn có thể chờ đợi là ManualResetEvent, và thậm chí tốt hơn, các ManualResetEventSlim.

Chúng có phương thức WaitOne() thực hiện chính xác điều đó. Bạn có thể chờ mãi hoặc đặt thời gian chờ hoặc "mã thông báo hủy" là cách để bạn quyết định ngừng chờ sự kiện (nếu bạn muốn hủy công việc của mình hoặc ứng dụng của bạn được yêu cầu thoát).

Bạn kích hoạt chúng gọi Set().

Here là tài liệu.

+1

Nhưng điều đó không trả về bất kỳ loại giá trị nào. –

+0

Bạn có thể chỉnh sửa qu và thêm tổng quan về các thành phần khác nhau và tương tác của chúng? Nó sẽ giúp chúng ta thấy rõ hơn những gì bạn đang cố gắng đạt được và hy vọng dẫn đến một thiết kế cải tiến. – Michael

-1

Nếu bạn hài lòng để sử dụng các phản ứng mở rộng của Microsoft, thì đây có thể làm việc độc đáo:

public class Foo 
{ 
    public delegate void MyEventHandler(object source, MessageEventArgs args); 
    public event MyEventHandler _event; 
    public string ReadLine() 
    { 
     return Observable 
      .FromEventPattern<MyEventHandler, MessageEventArgs>(
       h => this._event += h, 
       h => this._event -= h) 
      .Select(ep => ep.EventArgs.Message) 
      .First(); 
    } 
    public void SendLine(string message) 
    { 
     _event(this, new MessageEventArgs() { Message = message }); 
    } 
} 

public class MessageEventArgs : EventArgs 
{ 
    public string Message; 
} 

tôi có thể sử dụng nó như thế này:

var foo = new Foo(); 

ThreadPoolScheduler.Instance 
    .Schedule(
     TimeSpan.FromSeconds(5.0), 
     () => foo.SendLine("Bar!")); 

var resp = foo.ReadLine(); 

Console.WriteLine(resp); 

tôi cần phải gọi thông điệp SendLine trên một chủ đề khác nhau để tránh khóa, nhưng mã này cho thấy rằng nó hoạt động như mong đợi.

13

Nếu phương pháp hiện tại không đồng bộ thì bạn có thể sử dụng TaskCompletionSource. Tạo một trường mà trình xử lý sự kiện và phương thức hiện tại có thể truy cập.

TaskCompletionSource<bool> tsc = null; 

    private async void Button_Click(object sender, RoutedEventArgs e) 
    { 
     tsc = new TaskCompletionSource<bool>(); 
     await tsc.Task; 
     WelcomeTitle.Text = "Finished work"; 
    } 

    private void Button_Click2(object sender, RoutedEventArgs e) 
    { 
     tsc?.TrySetResult(true); 
    } 

Ví dụ này sử dụng biểu mẫu có khối chữ có tên WelcomeTitle và hai nút. Khi nút đầu tiên được nhấp, nút này sẽ bắt đầu sự kiện nhấp nhưng dừng ở đường chờ. Khi nút thứ hai được nhấp, tác vụ được hoàn thành và văn bản WelcomeTitle được cập nhật. Nếu bạn muốn để thời gian chờ cũng sau đó thay đổi

await tsc.Task; 

để

await Task.WhenAny(tsc.Task, Task.Delay(25000)); 
if (tsc.Task.IsCompleted) 
    WelcomeTitle.Text = "Task Completed"; 
else 
    WelcomeTitle.Text = "Task Timed Out"; 
+0

Bạn không cần đồng bộ để sử dụng TaskCompletionSource –

+0

Điều đó đúng, nhưng bạn cần nó nếu bạn muốn đợi Nhiệm vụ mà không chặn giao diện người dùng. – Adam

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