2010-02-21 35 views
6

Tôi có một số mã như dưới đây:Silverlight, đối phó với Async gọi

 foreach (var position in mAllPositions) 
     { 
       DoAsyncCall(position); 
     } 
//I want to execute code here after each Async call has finished 

Vậy làm thế nào tôi có thể làm các việc trên?
tôi có thể làm một cái gì đó như:

 while (count < mAllPositions.Count) 
     { 
      //Run my code here 
     } 

và đếm tăng sau mỗi cuộc gọi async được làm ... nhưng điều này không có vẻ như là một cách tốt để làm việc đó

Bất cứ lời khuyên? Có một số mẫu thiết kế cho vấn đề trên như tôi chắc chắn đó là một kịch bản phổ biến?

Trả lời

0

(NB, tôi đem lại cho bạn hai câu trả lời riêng biệt. Cái này dính càng gần với câu hỏi của bạn càng tốt.)

Câu hỏi của bạn nói

while{ count >= mAllPositions.Count) 
{ 
    //Run my code here 
} 

nhưng tôi đoán rằng những gì bạn thực sự có ý nghĩa là:

while(count < mAllPositions.Count) 
    ; // do nothing -- busy wait until the count has been incremented enough 
// Now run my code here 

??

Nếu vậy, sau đó bạn có thể thực hiện điều tương tự một cách hiệu quả hơn bằng cách sử dụng một semaphore:

Semaphore TheSemaphore = new Semaphore(0, mAllPositions.Count); 

Như mỗi cuộc gọi async hoàn tất, phát hành các semaphore.

TheSemaphore.Release(); 

Trước khi thực thi mã cuối cùng của bạn, đảm bảo các semaphore đã được phát hành số lượng cần thiết của thời đại:

for(int i=0; i<mAllPositions.Count; i++) 
    TheSemaphore.WaitOne(); 
// Now run follow-on code. 

Mã của bạn sẽ chặn cho đến khi hoạt động async đều được hoàn thành.

+0

Semaphore trên Silverlight? –

0

Câu hỏi của bạn là một chút không rõ ràng (nó phải được count <=?), Nhưng ở đây đi ...

gì bạn đang yêu cầu là làm thế nào để làm một đồng bộ gọi. Với cuộc gọi không đồng bộ, trước khi bạn thực hiện cuộc gọi, bạn chỉ định trình xử lý sự kiện sẽ được gọi sau khi hoàn thành cuộc gọi, sau đó bạn thực hiện cuộc gọi. Điều này có nghĩa là mã hoàn thành của bạn nằm trong một chức năng khác với mã thực hiện cuộc gọi. Điều này có nghĩa là nếu cuộc gọi không đồng bộ của bạn được thực hiện trên luồng giao diện người dùng, giao diện người dùng sẽ không chặn khi cuộc gọi được thực hiện.

Có thể thực hiện cuộc gọi đồng bộ trong Silverlight, nhưng bạn phải đảm bảo rằng bạn không thực hiện chúng trên chuỗi giao diện người dùng. Một cách để đạt được điều này là bắt đầu một chuỗi nền mới, thực hiện cuộc gọi async trên chuỗi nền, nhưng chặn trả lại cho đến khi cuộc gọi kết thúc. Đây là một mẫu mã giả:

private AutoResetEvent myResetEvent; 

private void MyCallFunction(object someParameter) { 

    if (this.Dispatcher.CheckAccess()) 
    { 
     Action<object> a = new Action<object>(MyCallFunction); 
     a.BeginInvoke(someParameter, null, null); 
     return; 
    } 

    myResetEvent = new AutoresetEvent(); 
    myAsyncCall.CallCompleted += new EventHandler<>(myAsyncCall_CallCompleted); 
    myAsyncCall.DoAsyncCall(someParameter); 

    myResetEvent.WaitOne(); 
    //increment your count here 
} 

private void myAsyncCall_CallCompleted(object sender, SomeEventArgs e) { 
    if (e.Error == null && !e.Cancelled) { 
     if (myResetEvent != null) 
      myResetEvent.Set(); 
    } 
} 

Lưu ý rằng mã này không đặc biệt là chủ đề an toàn hoặc sản xuất sẵn sàng - nó chỉ là một mẫu nhanh chóng.

Điều gì xảy ra ở đây là khi bạn nhập MyCallFunction, nó sẽ kiểm tra xem nó có đang chạy trên chuỗi giao diện người dùng hay không, nếu sau đó nó tự gọi lại trên chuỗi nền. Sau đó, nó thiết lập một AutoResetEvent và thực hiện cuộc gọi không đồng bộ.Sau đó nó dừng lại trên đối tượng myResetEvent cho đến khi nó được thiết lập (hoặc 'được báo hiệu') từ trình xử lý cuộc gọi đã hoàn thành, tại đó việc thực hiện mã điểm tiếp tục. Lưu ý rằng bạn không nên cố gắng truy cập một điều khiển trực tiếp từ mã như thế này mà không cần đảm bảo rằng bạn đã quay trở lại luồng giao diện người dùng.

Khi tôi bắt đầu tìm hiểu cách thực hiện việc này trong Silverlight, tôi bắt đầu với this SO post và tiếp tục với this link. Nhưng như Marc gravell nói trong bài viết đầu tiên, đừng làm điều đó nếu bạn có thể tránh được nó. Lần duy nhất tôi cần làm điều này là khi tôi cần tổng hợp kết quả của một vài cuộc gọi WCF khác nhau thành một kết quả được trả về giao diện người dùng (và những cuộc gọi này không thể kết hợp với một phương thức WCF mặt tiền).

0

(NB, tôi đưa ra hai câu trả lời riêng biệt, điều này mất một cách tiếp cận hoàn toàn khác so với trong câu hỏi.)

Tiếp theo mẫu mã do Miguel Madero tại this URL, sử dụng khung Reactive đợi song song cho tất cả kết quả để hoàn thành, sau đó tiếp tục với một nhiệm vụ khác.

(Có thảo luận khác trong chuỗi email tương tự, trong đó có một liên kết đến this closely related StackOverflow question.)

0

Bạn có thể làm những điều tuyệt vời bằng cách làm theo các khái niệm về quy trình công việc async (điều hướng đến C# đoạn) như mô tả thông qua blog của Tomas' .

http://tomasp.net/blog/csharp-async.aspx

0
  1. Thiết lập một AutoResetEvent tĩnh
  2. Cài đặt ThreadPoolQueueUserWorkItems
  3. ForEach mục trong vòng lặp, xếp hàng mặt hàng đó, và vượt qua trong AutoResetEvent như tham số của callback.
  4. Trong khi gọi lại, thực hiện công việc rồi gọi đến autoresetevent.Set();
  5. Trong mainbody tại điểm //I want to execute code here after each Async call has finished gọi autoResetEvent.Wait() thích hợp;
0

Một tùy chọn khác là tăng bộ đếm trong vòng lặp foreach gọi các quá trình không đồng bộ. Sau đó, trong kiểm tra gọi lại, bạn sẽ giảm bộ đếm và kiểm tra tính bình đẳng về 0. Khi bộ đếm đã trở về 0, tất cả các cuộc gọi đã hoàn tất và mã tiếp theo của bạn có thể chạy. Chỉ cần chắc chắn rằng bạn thay đổi truy cập một cách an toàn thread. Cách dễ nhất để thực hiện việc này có thể là quay lại luồng giao diện người dùng trước khi bạn giảm và kiểm tra bộ đếm.

0

Tôi không thấy điều này là sự cố "Tôi muốn thực hiện cuộc gọi đồng bộ". Tất cả những gì bạn đang thực sự làm là kết hợp một loạt các cuộc gọi không đồng bộ với nhau khi chúng hoàn tất.

Tôi đã gặp phải điều này trước đây trong trường hợp bạn có danh sách các mục và cần lấy các thuộc tính cho chúng một cách không đồng bộ từ máy chủ (ví dụ: một số trạng thái) nhưng cũng muốn cập nhật một số chỉ báo tiến trình tải trên màn hình .

Trong trường hợp này, bạn không muốn chặn chuỗi giao diện người dùng trong khi tất cả các mục đang được cập nhật (ngăn chặn chuỗi vẫn là ác nếu có thay thế).

Vì vậy, tôi đã sử dụng một lớp kiểm soát như thế này:

public class GroupAction 
{ 
    public delegate void ActionCompletion(); 

    private uint _count = 0; 
    private ActionCompletion _callback; 
    private object _lockObject = new object(); 

    public GroupAction(uint count, ActionCompletion callback) 
    { 
     _count = count; 
     _callback = callback; 
    } 

    public void SingleActionComplete() 
    { 
     lock(_lockObject) 
     { 
      if (_count > 0) 
      { 
       _count--; 
       if (_count == 0) _callback(); 
      } 
     } 
    } 
} 

Bạn tạo hành động này với một số (đối với số hạng mục) và một callback mà được thực hiện khi hoàn thành tất cả các mục.Về cơ bản giống như một semaphore với kiểm soát nhiều hơn và không có luồng để lo lắng về.

Mã gọi sẽ trông giống như thế này (Tôi đã dựa trên ví dụ về việc gọi dịch vụ web chuyển đổi tạm thời chuẩn mà bạn có thể tìm thấy tại w3schools.com).

private void DoGroupCall() 
    { 
     uint count = 5; 
     GroupAction action = new GroupAction(count, 
      () => 
      { 
       MessageBox.Show("Finished All Tasks"); 
      }); 

     for (uint i = 0; i < count; i++) 
     { 
      TempConvertHttpPost proxy = new TempConvertHttpPostClient(new BasicHttpBinding(), new EndpointAddress("http://localhost/webservices/tempconvert.asmx")); 
      CelsiusToFahrenheitRequest request = new CelsiusToFahrenheitRequest() { Celsius = "100" }; 

      proxy.BeginCelsiusToFahrenheit(request, 
        (ar) => Deployment.Current.Dispatcher.BeginInvoke(
        () => 
         { 
          CelsiusToFahrenheitResponse response = proxy.EndCelsiusToFahrenheit(ar); 

          // Other code presumably... 

          action.SingleActionComplete(); 
         } 
       ), null); 
     } 
    } 

Lưu ý cách thể hiện GroupAction được xác định với đại biểu gọi lại nội tuyến và sau đó chức năng SingleCallBack được gọi mỗi lần dịch vụ trả về.

Hope this helps ...

1

Cho rằng bạn đang sử dụng Silverlight, và bạn không có quyền truy cập vào Semaphores, bạn có thể tìm kiếm một cái gì đó như thế này (viết bằng notepad, vì vậy không hứa hẹn như cú pháp hoàn hảo):

int completedCallCount = 0; 
int targetCount = mAllPositions.Count; 

using (ManualResetEvent manualResetEvent = new ManualResetEvent(false)) 
{ 
    proxy.DoAsyncCallCompleted += (s, e) => 
    { 
     if (Interlocked.Increment(ref completedCallCount) == targetCount) 
     { 
      manualResetEvent.Set(); 
     } 
    }; 

    foreach (var position in mAllPositions) 
    { 
     proxy.DoAsyncCall(position); 
    } 

    // This will wait until all the events have completed. 
    manualResetEvent.WaitOne(); 
} 

Tuy nhiên, một trong những lợi ích của Silverlight buộc bạn phải tải không đồng bộ dữ liệu là bạn phải làm việc chăm chỉ để khóa UI lên khi thực hiện một cuộc gọi dịch vụ, đó là chính xác những gì bạn sẽ làm với mã như thế này, trừ khi bạn có nó chạy trên một chủ đề nền, wh Tôi khuyên bạn nên làm. Bạn luôn có thể hiển thị thông báo "vui lòng đợi" cho đến khi quá trình nền hoàn tất.

+0

Cách tốt nhất để triển khai và sử dụng thông báo "Vui lòng đợi ..." trong cuộc gọi không đồng bộ là gì và nếu có nhiều cuộc gọi không đồng bộ. – VoodooChild

+0

Tôi đã viết Semaphores cho Silverlight: http://modosansreves-coding.blogspot.com/2011/08/semaphore-in-silverlight.html –

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