8

Tôi đang chuyển một số mã từ khung .NET đầy đủ sang phiên bản WP7 và tôi đang gặp sự cố với các cuộc gọi đồng bộ hóa với async.Thực hiện các cuộc gọi đồng bộ trong Silverlight WP7

string response; 
string requestString = GenerateReqString(); 
HttpWebRequest req = (HttpWebRequest) WebRequest.Create("endpoint"); 
req.Method = "POST"; 
req.ContentType = "text/xml"; 

req.ContentLength = requestString.Length; 

StreamWriter sw = new StreamWriter (req.GetRequestStream(), System.Text.Encoding.ASCII); 
sw.Write(requestString); 
sw.Close(); 

StreamReader sr = new StreamReader(req.GetResponse().GetResponseStream()); 
response = sr.ReadToEnd(); 
sr.Close(); 

Chuỗi phản hồi sau đó được phân tích thành danh sách đối tượng được trả về bởi phương pháp.

Sự cố tôi gặp phải là không có cách nào để thực hiện cuộc gọi đồng bộ trong Silverlight/WP7. Nếu tôi sử dụng một cuộc gọi trở lại, tôi sẽ nhận được phản hồi trong một hàm khác và sẽ không thể trả về nó từ hàm ban đầu. Có cách nào để thực hiện cuộc gọi một cách đồng bộ hoặc trả về từ chức năng CallBack trở lại phương thức đã khởi động cuộc gọi không đồng bộ không?

Trả lời

11

Bạn cần suy nghĩ về vấn đề khác nhau. Để làm cho những thứ không đồng bộ "cảm thấy" đồng bộ, cách dễ nhất để thực hiện việc đó là cấu trúc lại mã của bạn để sử dụng 'continuation passing style'.

Về bản chất, thay vì gọi hàm trả về giá trị và sau đó bạn xử lý giá trị đó, bạn gọi hàm, chuyển hàm ẩn danh làm đại biểu cho nó. Hàm được gọi sau đó sẽ gọi đại biểu, đi qua trong chuỗi.

Dưới đây là một ví dụ, trong đó sử dụng chức năng ẩn danh và lambdas:

void DoSomethingAsync(Action<string> callback) { 
    HttpWebRequest req; // TODO: build your request 

    req.BeginGetResponse(result => { 
     // This anonymous function is a closure and has access 
     // to the containing (or enclosing) function. 
     var response = req.EndGetResponse(result); 

     // Get the result string and call the callback 
     string resultString = null; // TODO: read from the stream 

     callback(resultString); 
    }, null); 
} 

Đây là một nửa dung dịch. Phần tiếp theo, là thực sự gọi điều này. Hãy tưởng tượng bạn có một phiên bản ICommand hoặc đơn giản hơn, một sự kiện bấm nút cần thiết để gọi hàm này và "lấy chuỗi". Thay vì "nhận chuỗi", bạn gọi hàm này và cung cấp một phương thức gọi lại (đó sẽ là một đóng).

void btnGo_Click(object sender, EventArgs e) { 
    DoSomethingAsync(resultString => { 
     // This anonymous function is called when the web request has 
     // finished and has your string. 

     // Now that we have the string, we can go and process it. 
     ProcessWebResponseResult(resultString); 
    }); 
} 

Dưới đây là một bài viết thực sự tốt giải thích các khái niệm thêm: http://blogs.msdn.com/b/wesdyer/archive/2007/12/22/continuation-passing-style.aspx

+0

Cho phép nói ProcessWebResponseResult() tạo đối tượng từ chuỗi và hàm gọi hàm DoSomethingAsync() cần trả về đối tượng đó. Điều đó có thể không? – CACuzcatlan

+0

Đó là thiếu điểm. Phương thức gọi DoSomethingAsync chuyển một hàm đến DoSomethingAsync. Hàm được truyền là "phải làm gì với kết quả của hàm này". Khi bạn cần nhiều hơn, bạn thêm các chức năng tiếp tục bổ sung vào chuỗi cuộc gọi của mình. –

+0

Đây là cách bạn nghĩ về một chương trình thường chạy: 'A(); B(); C();'. Tuy nhiên, khi bạn thực hiện với 'kiểu chuyển tiếp liên tục', bạn sẽ cho mỗi hàm biết phải làm gì khi hoàn thành. Thay vì "Tôi nhận được kết quả của tôi từ chức năng này", bạn nói "Tôi nói chức năng này phải làm gì với kết quả của nó" thông qua một đại biểu. –

2

Đầu tiên tôi khuyên bạn nên cố gắng để có được thoải mái với async nhưng nếu bạn thực sự muốn/cần phải chuyển đổi cuộc gọi không đồng bộ để đồng bộ bạn có thể sử dụng ManualResetEvent để đạt được kết quả mong muốn.

Dưới đây là một ví dụ nhanh nếu sử dụng:

public ManualResetEvent _event = new ManualResetEvent(false); 

... 
{ 
    ... 
    var wc = new WebClient(); 
    wc.OpenReadCompleted += new OpenReadCompletedEventHandler(ReadCompleted); 
    wc.OpenReadAsync(uri); 

    // block until async call is complete 
    _event.WaitOne(); 
} 

private static void ReadCompleted(object sender, OpenReadCompletedEventArgs e) 
{ 
    ... 
    // set event to unblock caller 
    _event.Set(); 
} 

Bây giờ bạn mã sẽ chặn trên dòng _event.WaitOne(); cho đến khi _event.Set(); được gọi.

Chúc may mắn!

-2

Tốt hơn nên làm điều này một cách không đồng bộ để người dùng có thể tiếp tục tương tác với thiết bị.

Tôi nghi ngờ lý do bạn muốn thực hiện việc này là ngăn người dùng tương tác với các điều khiển nhất định cho đến khi yêu cầu của bạn hoàn tất.

Cách được chấp nhận để tiếp cận điều này là ẩn hoặc tắt các phần tử giao diện người dùng không được tương tác trong quá trình xử lý yêu cầu.

+0

Tốt hơn hết là đã trừu tượng hóa tất cả điều này và bắt đầu thực hiện nó một cách không đồng bộ ở mức cao hơn để bắt đầu, tại thời điểm đó, bạn sẽ muốn thực hiện yêu cầu cấp thấp của mình một cách đồng bộ như bạn đang ở trên một số loại chuỗi công nhân. –