2013-03-26 47 views
1

有沒有辦法同時調用多種方法?我有一個在加載數據加載數量驚人的WinForms應用程序:異步調用多種方法

private void form1_Load(object sender, EventArgs e) 
{ 
    LoadValues1(); 
    LoadValues2(); 
    LoadValues3(); 
    LoadValues4(); 
    LoadValues5(); 
    LoadValues6(); 
    LoadValues7(); 
    LoadValues8(); 
} 

這些方法檢索數據和填充的DevExpress LookUpEdits(類似於Windows dropdownlists),所以他們都看起來像這樣:

DBContext dbContext = new DBContext(); 
ObservableCollection<string> values1 = 
    new ObservableCollection<string>((from i in dbContext.Items 
             where i.Value1 != null 
             && i.Value1.Length > 0 
             orderby i.Value1 
             select i.Value1).Distinct()); 

lookupValues1.Properties.DataSource = descModelYear; 
DevExpress.XtraEditors.Controls.LookUpColumnInfoCollection colInfo = lookupValues1.Properties.Columns; 
colInfo.Clear(); 
colInfo.Add(new DevExpress.XtraEditors.Controls.LookUpColumnInfo("Column")); 
colInfo[0].Caption = "Values 1"; 

和一些方法需要一段時間才能完成,但他們都不是彼此依賴,所以我想我可以做他們都在同一時間:

Task.Factory.StartNew(() => LoadValues1()); 
Task.Factory.StartNew(() => LoadValues2()); 
etc. 

BU t當第二個任務運行時,我不斷收到錯誤信息,稱控件不能從與創建它不同的線程訪問。

任何幫助表示讚賞!

+0

在方法'LoadValuesXXX'你試圖操縱UI(現在從另一個線程)這是不好的。有數百個關於此主題的現有問題/答案。 – spender 2013-03-26 18:01:43

+0

一旦你解決了交叉線程問題,你可能想知道你可以使用一個縮寫來在一個單獨的線程中運行一組動作,並等待它們完成,如下所示:'Parallel.Invoke( '1,action2,action3,action4,action5);' – 2013-03-26 18:39:57

回答

2

正如其他人已經指出,你需要確保訪問UI元素時,你需要確保你正確閱讀。這是很容易使用任務並行庫,像這樣的事:

private TaskScheduler m_TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext(); 

一旦你的的TaskScheduler你可以安排你的任務,到UI線程像這樣運行:

Task.Factory.StartNew(() => 
{ 
    LoadValues1(); 
}, CancellationToken.None, TaskCreationOptions.None, m_TaskScheduler); 

需要說明這裏是在UI線程上運行所有任務可能仍然會將問題鎖定在您身上。我建議你創建一些類或一組集合,你可以填充相關信息,然後一旦所有的數據都在那裏,你可以調用UI線程上的另一個方法,它將控件綁定到數據或做任何你需要的UI特定操作。

+0

謝謝你,但是當我實現它時,我會在聲明中看到以下錯誤:「當前的SynchronizationContext不能用作任務調度程序」 – Robert 2013-03-26 18:37:08

+0

當你嘗試設置m_TaskScheduler?如果是這樣的話,你在代碼中試圖獲取當前的同步上下文?我通常使用WinForm的WPF,但我會想象它應該在您的表單加載 – 2013-03-26 18:38:43

+0

是的....我宣佈它在頂部,並試圖讓它在Form_Load – Robert 2013-03-26 18:40:49

1

在操作UI控件時,必須在創建控件的同一個線程上這樣做。在這種情況下,您應該只運行那些同步修改UI的方法。如果所有方法都修改了用戶界面,則應該拆分工作,以便只有修改UI的代碼在UI線程上被調用。

一般來說,你可以用InvokeRequired來檢查你是否在正確的線程上,並且Invoke在UI線程上運行一個方法。

0

你應該分開你的邏輯在不同的單位,並嘗試讓你的UI邏輯在一個地方。

//private fields 
private readonly DBContext _dbContext = new DBContext(); 

private Task<ICollection<string>> GetValues1() 
{ 
    return Task.Run(() => 
      { 
       return (from i in _dbContext.Items 
         where i.Value1 != null 
         && i.Value1.Length > 0 
         orderby i.Value1 
         select i.Value1) 
         .Distinct() 
         .ToList(); 
      }; 
} 

private void LoadUIElements1(ICollection<string> values) 
{ 
    var observableValues = new ObservableCollection<string>(values); 

    lookupValues1.Properties.DataSource = descModelYear; 
    DevExpress.XtraEditors.Controls.LookUpColumnInfoCollection colInfo = lookupValues1.Properties.Columns; 
    colInfo.Clear(); 
    colInfo.Add(new DevExpress.XtraEditors.Controls.LookUpColumnInfo("Column")); 
    colInfo[0].Caption = "Values 1"; 
} 

private async void form1_Load(object sender, EventArgs e) 
{ 
    var tasks = new List<Task>(); 

    // Start each UI task so they will complete independently. 
    var uiTask1 = GetValues1() 
      .ContinueWith(t => 
       LoadUIElements1(t.Result), 
       CancellationToken.None, 
       TaskCreationOptions.None, 
       TaskScheduler.FromCurrentSynchronizationContext()); 
    tasks.Add(uiTask1); 

    // Wait for all the tasks to complete 
    Task.WaitAll(tasks.ToArray()); 
    tasks.Clear(); 
} 
+0

在form_load事件中,您有'LoadUIElements1(t.Result)',但我沒有看到它的定義位置。我試過'LoadValues1',但它抱怨t.Result被傳入。 – Robert 2013-03-26 18:39:23

+0

它應該是'LoadValues1'。我改變了'LoadUIElements1'的簽名。 – Romoku 2013-03-26 19:02:36