2012-12-20 29 views
5

Tôi đang cố gắng hiểu mã đúng để lấy một tập hợp dữ liệu không đồng bộ khi tôi không có quyền truy cập vào lib khách hàng mà tôi đang sử dụng để truy xuất dữ liệu. Tôi chỉ định điểm cuối và phạm vi ngày và tôi phải truy xuất danh sách danh sách phát. Những gì tôi có bây giờ không bao giờ trở lại sau khi cuộc gọi Start(). Lưu ý: điều này đang chạy trong một WinForm. Tôi đang cố gắng hiểu rõ hơn về Nhiệm vụ và không chỉ muốn chuyển sang chờ đợi hoặc là một Người làm nền tảng. Tôi biết tôi bị lạc đâu đó.C# và Tasks - UI Thread Hang - Pre-Async/Từ khóa chờ đợi

private void GoButtonClick(object sender, EventArgs e) 
    { 
     string baseUrl = "http://someserver/api"; 
     var startDateTime = this._startDateTimePicker.Value; 
     var endDateTime = this._endDateTimePicker.Value; 
     _getPlaylistsFunc = delegate() 
      { 
       var client = new PlaylistExportClient(baseUrl); 
       return client.GetPlaylistsByDateRange(startDateTime, endDateTime).ToList(); 
      }; 
     var task = new Task<List<Playlist>>(_getPlaylistsFunc); 
     task.ContinueWith((t) => DisplayPlaylists(t.Result)); 
     task.Start(); 
    } 

    private void DisplayPlaylists(List<Playlist> playlists) 
    { 
     _queueDataGridView.DataSource = playlists; 
    } 

CẬP NHẬT tôi đã thực hiện những thay đổi này nhưng bây giờ đăng ký có vẻ để treo thread UI.

private void GoButtonClick(object sender, EventArgs e) 
    { 
     string baseUrl = "http://someserver/api"; 
     var startDateTime = this._startDateTimePicker.Value; 
     var endDateTime = this._endDateTimePicker.Value; 
     var token = Task.Factory.CancellationToken; 

     var context = TaskScheduler.FromCurrentSynchronizationContext(); 
     Task.Factory.StartNew(() => 
      { 
       var client = new PlaylistExportClient(baseUrl); 
       _queueDataGridView.DataSource = client.GetPlaylistsByDateRange(startDateTime, endDateTime).ToList(); 

      },token,TaskCreationOptions.None,context); 

    } 
+0

Nếu bạn đặt điểm ngắt trong 'GetPlaylistsByDateRange', bạn có thấy rằng nó thực sự được gọi không? – user7116

+2

Bạn sẽ cần phải vượt qua một bối cảnh đồng bộ hóa để tiếp tục để nó chạy trong chuỗi giao diện người dùng thay vì một chuỗi chủ đề khác. Ngoài ra, tôi không thấy bất kỳ lỗi thực sự nào ở đây. – Servy

+2

Bản cập nhật của bạn chạy * tất cả * tác vụ trên chuỗi giao diện người dùng. Chỉ có người thứ hai nên chạy ở đó. –

Trả lời

2

Có vẻ như bạn đang gán cho một tài sản của một điều khiển giao diện người dùng trong một thread nền. Đó thường là tin xấu. WPF thường ném một ngoại lệ khi bạn làm điều đó, không chắc chắn về WinForms.

Ghi dữ liệu trong chuỗi nền, nhưng chuyển về chủ đề giao diện người dùng chính trước khi gán cho một điều khiển giao diện người dùng. Hãy thử đăng dữ liệu lên chuỗi giao diện người dùng bằng cách sử dụng một cái gì đó như

var uiSync = SynchronizationContext.Current; 
    Task.Factory.StartNew(() => 
     { 
      var client = new PlaylistExportClient(baseUrl); 
      var list = client.GetPlaylistsByDateRange(...).ToList(); 
      uiSync.Post(() => _queueDataGridView.DataSource = list, null); 
     },token,TaskCreationOptions.None,context); 
+0

Hmm. Tôi hiểu - tôi nghĩ vậy. .Post() không phải là một phương pháp trên bối cảnh của tôi mặc dù. – BuddyJoe

+0

Woops. Ok, những gì bạn đang gọi một biến ngữ cảnh thực sự là một bộ lập lịch biểu. Tôi sẽ cập nhật câu trả lời của tôi với mã để lấy bối cảnh đồng bộ hóa – dthorpe

+0

Có vẻ như điều đó thực sự gần gũi.Cảm ơn tôi chỉ đang làm việc với đối số lambda Post() ngay bây giờ IDE đang báo cáo lại chữ ký không đúng trên hàm ẩn danh – BuddyJoe

3

Tôi khuyên bạn nên sử dụng Mẫu không đồng bộ dựa trên nhiệm vụ. Nó khá đơn giản:

private async void GoButtonClick(object sender, EventArgs e) 
{ 
    string baseUrl = "http://someserver/api"; 
    var startDateTime = this._startDateTimePicker.Value; 
    var endDateTime = this._endDateTimePicker.Value; 
    var playlists = await Task.Run(() => 
    { 
     var client = new PlaylistExportClient(baseUrl); 
     return client.GetPlaylistsByDateRange(startDateTime, endDateTime).ToList(); 
    }); 
    _queueDataGridView.DataSource = playlists; 
} 

Lưu ý rằng điều này sẽ chặn một sợi chỉ; nếu bạn có thể sửa đổi thư viện để có phương thức GetPlaylistsByDateRangeAsync, bạn có thể làm cho điều này hiệu quả hơn.

Edit: Nếu bạn đang bị mắc kẹt trên .NET 4.0, bạn có thể cài đặt Microsoft.Bcl.Async để có được đầy đủ async/await khả năng. Nếu - vì một lý do không thể giải thích - bạn vẫn không thể sử dụng async/await, sau đó bạn có thể làm điều đó như thế này:

private void GoButtonClick(object sender, EventArgs e) 
{ 
    string baseUrl = "http://someserver/api"; 
    var startDateTime = this._startDateTimePicker.Value; 
    var endDateTime = this._endDateTimePicker.Value; 
    var context = TaskScheduler.FromCurrentSynchronizationContext(); 
    Task.Run(() => 
    { 
     var client = new PlaylistExportClient(baseUrl); 
     return client.GetPlaylistsByDateRange(startDateTime, endDateTime).ToList(); 
    }).ContinueWith(t => 
    { 
     _queueDataGridView.DataSource = t.Result; 
    }, context); 
} 

Tuy nhiên, lưu ý rằng xử lý lỗi của bạn là phức tạp hơn với phương pháp này.

+0

Tôi đã cố gắng tìm hiểu làm thế nào điều này được thực hiện trước async/awaits.Nhưng cũng có thông tin tốt để có. – BuddyJoe

+0

Tôi đưa ra câu trả lời này dựa trên thẻ 'async-await' được chỉ định trên câu hỏi.Nếu bạn muốn câu trả lời trước 'đồng bộ', hoặc' Task.ContinueWith' (cùng với 'TaskScheduler.FromCurrentSynchronizationContext') hoặc' BackgroundWorker' sẽ 'async' code là sạch hơn nhiều so với một trong các tùy chọn này. –

+0

Xem câu trả lời cập nhật cho mã bằng cách sử dụng phương pháp 'Tiếp tụcWith' –

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