2010-11-02 79 views
7

Câu hỏi này kết hợp hai chủ đề tôi không hoàn toàn hiểuAgent/MailboxProcessor trong C# sử dụng async mới/chờ đợi

Đọc qua một paper về async trong F #, tôi đi qua các chủ đề của Đại lý/MailboxProcessors, mà có thể được sử dụng để thực hiện các máy trạng thái phản ứng. Có thể chức năng async/await mới trong C# 5 được sử dụng để thực hiện một cái gì đó tương tự trong C#, hoặc là đã có một cái gì đó tương tự mà sẽ phù hợp hơn?

+0

Bạn có thể sửa liên kết bị hỏng không? – czifro

+0

@czifro, ok. – Benjol

Trả lời

3

Về nguyên tắc, tôi hy vọng sẽ đơn giản để dịch các API F # này thành C# -plus-async-await. Trong thực tế, tôi không rõ liệu nó có xuất hiện đẹp, hay xấu xí và đầy đủ các chú thích kiểu bổ sung, hay đơn giản là không thành ngữ và cần một số xoa bóp API để làm cho nó cảm thấy thoải mái hơn ở nhà trong C#. Tôi nghĩ rằng bồi thẩm đoàn là cho đến khi một người nào đó thực hiện công việc và thử nó. (Tôi đoán là không có mẫu như vậy trong CTP đang chờ.)

+2

Đã tải xuống CTP. Bây giờ tôi sẽ tiến hành mở rộng sự thiếu hiểu biết của tôi bằng cách thử :) – Benjol

+0

Tôi thấy Siêu Breakaway của bạn đã biến nó thành các mẫu, xin chúc mừng :) – Benjol

+1

Ouch! Quá khó đối với tôi công cụ này :( – Benjol

11

Với một chút hack khá khủng khiếp, bạn có thể sử dụng loại MailboxProcessor từ C# sử dụng async. Một số khó khăn là loại sử dụng một số tính năng cụ thể F # (đối số tùy chọn là các tùy chọn, chức năng là FSharpFunc loại, v.v.)

Về mặt kỹ thuật, sự khác biệt lớn nhất là đồng bộ F # được dealyed trong khi C# async tạo một tác vụ đang chạy . Điều này có nghĩa là để xây dựng F # async từ C#, bạn cần phải viết một phương thức mất unt -> Task<T> và tạo ra Async<T>. Tôi đã viết một blog post that discusses the difference.

Anwyay, nếu bạn muốn thử nghiệm, đây là một số mã, bạn có thể sử dụng:

static FSharpAsync<T> CreateAsync<T>(Func<Task<T>> f) 
{ 
    return FSharpAsync.FromContinuations<T>(
    FuncConvert.ToFSharpFunc< 
     Tuple< FSharpFunc<T, Unit>, 
      FSharpFunc<Exception, Unit>, 
      FSharpFunc<OperationCanceledException, Unit> >>(conts => { 
    f().ContinueWith(task => { 
     try { conts.Item1.Invoke(task.Result); } 
     catch (Exception e) { conts.Item2.Invoke(e); } 
    }); 
    })); 
} 

static void MailboxProcessor() { 
    var body = FuncConvert.ToFSharpFunc< 
       FSharpMailboxProcessor<int>, 
       FSharpAsync<Unit>>(mbox => 
    CreateAsync<Unit>(async() => { 
     while (true) { 
     var msg = await FSharpAsync.StartAsTask 
      (mbox.Receive(FSharpOption<int>.None), 
      FSharpOption<TaskCreationOptions>.None, 
      FSharpOption<CancellationToken>.None); 
     Console.WriteLine(msg); 
     } 
     return null; 
    })); 
    var agent = FSharpMailboxProcessor<int>.Start(body, 
       FSharpOption<CancellationToken>.None); 
    agent.Post(1); 
    agent.Post(2); 
    agent.Post(3); 
    Console.ReadLine(); 
} 

Như bạn có thể thấy, điều này có vẻ :-) thực sự khủng khiếp.

  • Về nguyên tắc, nó có thể là có thể viết một wrapper C# thân thiện cho các loại MailboxProcessor (chỉ cần giải nén các bit xấu xí từ mã này), nhưng có một số vấn đề.

  • Trong F #, bạn thường sử dụng các ký tự đồng bộ đuôi đệ quy để triển khai máy trạng thái trong bộ xử lý hộp thư. Nếu bạn viết cùng một thứ trong C#, cuối cùng bạn sẽ nhận được StackOverflow, vì vậy bạn cần phải viết các vòng lặp với trạng thái có thể thay đổi.

  • Hoàn toàn có thể viết tác nhân trong F # và gọi nó từ C#. Đây chỉ là vấn đề hiển thị giao diện thân thiện với C# từ F # (sử dụng phương thức Async.StartAsTask).

+0

Ouch, mắt của tôi :) Tác vụ -> Async 'chính xác là bit mà tôi gặp khó khăn, vì vậy tôi sẽ xem xét điều đó.Làm thế nào về viết lại một MailboxProcessor trong C#, hoặc nó sẽ không chỉ là giá trị nỗ lực vì điểm bullet thứ hai của bạn về đệ quy? – Benjol

+0

@Benjol - Tôi đã viết một loại tương tự trong C#. Đó là khoảng 300 dòng mã. Sự khác biệt là một API đơn giản hóa. – ChaosPandion

+0

@Benjol: Tôi nghĩ rằng viết lại MailboxProcessor trong C# cũng sẽ là một lựa chọn. Nó không phải là dễ dàng để có được quyền khóa và đồng bộ hóa (đặc biệt là cho 'TryScan' phương pháp), nhưng tôi nghĩ rằng nó sẽ làm việc (Bạn luôn có thể tránh đệ quy, nhưng trong trường hợp _state machines_ mã trông khá xấu, vì vậy nó là không may nếu đó là những gì người dùng phải viết) –

0

Bạn có thể xem qua số Stact. Nó đã không được cập nhật trong một thời gian ngắn, nhưng nếu bạn muốn làm điều gì đó với sự hỗ trợ C# tốt hơn một chút, bạn có thể thấy nó là một điểm khởi đầu tốt. Tôi không nghĩ rằng nó là up-to-date với async/await, mặc dù.

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