2013-03-26 48 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