2012-11-27 31 views
7

Tôi đang tìm một cách đơn giản để thực hiện tác vụ ở chế độ nền, sau đó cập nhật nội dung nào đó (trên chuỗi chính) khi hoàn thành. Nó thuộc tầng lớp 'mô hình' mức thấp nên tôi không thể gọi InvokeOnMainThread vì tôi không có NSObject xung quanh. Tôi có phương pháp này:C# chủ đề nền đơn giản

public void GetItemsAsync(Action<Item[]> handleResult) 
{ 
    Item[] items=null; 
    Task task=Task.Factory.StartNew(() => 
    { 
    items=this.CreateItems(); // May take a second or two 
    }); 
    task.ContinueWith(delegate 
    { 
    handleResult(items); 
    }, TaskScheduler.FromCurrentSynchronizationContext()); 
} 

Điều này dường như làm việc OK, nhưng

1) Đây có phải là tốt nhất (đơn giản nhất) cách?

2) Tôi đang lo lắng về biến cục bộ:

Item{} items=null 

ngừng mà biến mất khi trở về phương pháp trước khi thread nền hoàn gì?

Cảm ơn.

Trả lời

2

Tôi nghĩ rằng phương pháp của bạn hơi vi phạm một Single Responsibility Principle, bởi vì nó làm quá nhiều.

Trước hết tôi đề nghị thay đổi CreateItems trở Task thay vì gói nó trong GetItemsAsync:

public Task<Item[]> CreateItems(CancellationToken token) 
{ 
    return Task.Factory.StartNew(() => 
    // obtaining the data... 
    {}); 
} 

CancellationToken là không bắt buộc nhưng có thể giúp bạn nếu bạn có thể hủy bỏ này hoạt động dài chạy.

Với phương pháp này, bạn có thể loại bỏ hoàn toàn GetItemsAsync vì nó đơn giản như vậy để xử lý kết quả theo khách hàng của bạn mà không đi đại biểu này:

// Somewhere in the client of your class 
var task = yourClass.CreateItems(token); 
task.ContinueWith(t => 
// Code of the delegate that previously 
// passed to GetItemsAsync method 
{}, TaskScheduler.FromCurrentSynchronizationContext()); 

Sử dụng phương pháp này, bạn sẽ nhận được mã rõ ràng hơn chỉ với một trách nhiệm. Task chính lớp là công cụ hoàn hảo để biểu diễn hoạt động không đồng bộ dưới dạng first class object. Sử dụng kỹ thuật được đề xuất, bạn có thể dễ dàng giả lập bạn thực hiện hiện tại bằng hành vi giả mạo để kiểm tra đơn vị mà không thay đổi mã máy khách của bạn.

+0

Cảm ơn, đó là nguyên tắc tốt hơn. Thậm chí có thể xóa biến tác vụ yourClass.CreateItems (mã thông báo) .ContinueWith (t => { }, TaskScheduler.FromCurrentSynchronizationContext()); Thật đáng tiếc, chúng ta cần "TaskScheduler.FromCurrentSynchronizationContext()". Tôi muốn API đơn giản nhất có thể cho khách hàng nên nó hoặc là: 1) yourClass.GetItemsAsync (đại biểu (Item [] mục) {\t // Do something với mục }); hoặc 2) yourClass.CreateItems (.) ContinueWith (t => { // Do something với t.Result }, TaskScheduler.FromCurrentSynchronizationContext()); Vì vậy, tôi vẫn cần phương thức GetItemsAsync thực sự. – Bbx

+0

Vâng, bạn nói đúng. Nhưng GetItemsAsync rõ ràng hơn nhiều so với giải pháp trước đây về các nguyên tắc OO và lập trình dựa trên tác vụ. Ví dụ, bằng cách sử dụng kỹ thuật được đề xuất, nó sẽ dễ di chuyển hơn nhiều để chờ đợi/các tính năng không đồng bộ C# 5.0. Trong điều khoản của OOP giải pháp này là đơn giản hơn bởi vì nó không cố gắng để phát minh ra bánh xe. Và đừng quên một testability: trong trường hợp này bạn có thể dễ dàng có được nhiệm vụ này từ thực hiện giả và kiểm tra bất kỳ trường hợp góc của bạn mã khách hàng. –

1

Mã của bạn có vẻ ổn.

Đây là ví dụ hoàn hảo về thời điểm sử dụng async/await nếu bạn có thể sử dụng C# 5, nhưng nếu không, bạn phải viết nó như bạn đã làm với phần tiếp theo.

Biến số items được ghi lại trong lambdas của bạn, vì vậy cũng tốt.

Khi bạn viết một lambda sử dụng một biến bên ngoài, trình biên dịch C# tạo ra một lớp có chứa biến. Điều này được gọi là đóng cửa và có nghĩa là bạn có thể truy cập biến bên trong lambda của bạn.

+0

Cảm ơn bạn đã trả lời nhanh :) Tôi gần như đã từ bỏ SO sau câu hỏi cuối cùng về API iOS để tìm hiểu lưu lượng truy cập dữ liệu hoàn toàn bị bỏ qua. Tôi đoán nó tốt cho một số thứ chứ không phải những thứ khác. – Bbx

1

Đây là một soultion đẹp hơn:

public void GetItemsAsync(Action<Item[]> handleResult) 
    { 
     var task = Task.Factory.StartNew<Item[]>(() => 
     { 
      return this.CreateItems(); // May take a second or two 
     }); 

     task.ContinueWith(delegate 
     { 
      handleResult(task.Result); 
     }, TaskScheduler.FromCurrentSynchronizationContext()); 
    } 
+0

Cảm ơn, điều đó đẹp hơn :) – Bbx

+0

@Bbx: bạn không có tính năng Kết quả vì bạn nên thay đổi mã thành: Task task = Task.Factory .... –

+0

Cảm ơn, yea Tôi vừa nhận ra rằng sau khi tôi đăng bình luận của tôi :) – Bbx

2

Something như thế này:

public void GetItemsAsync(Action<Item[]> handleResult) 
    { 
     int Id = 11; 
     Task<Item[]> task = Task.Factory.StartNew(() => CreateItems(Id)); // May take a second or two 
     task.ContinueWith(t => handleResult(t.Result), TaskScheduler.FromCurrentSynchronizationContext()); 
    } 
+0

Đáng yêu, cảm ơn. Nhưng cá nhân, tôi thích @ laszlokiss88 câu trả lời - nó cũng chỉ là 2 dòng (về cơ bản) và có vẻ đơn giản với tôi. – Bbx