目前,我有一个包含一堆 id 的 List 字符串 ids、一个以空开头并填充有对象的 List 对象,以及一个函数返回一个对象 getObject(string id),该对象获取给定 id 字符串的对象。有没有一种方法可以轻松地进行线程化,而不必使用 Threading 类连续进行?线程似乎没有办法返回对象,因为它们使用 void 函数。
3 回答
我展示了两种方法来做到这一点,一种是“老式”方式,一种是任务。他们都在一个简单的演示 WinForms 应用程序中(只是因为......)
这是老式的方式(将工作显式提交到线程池)。为此,我创建了一个简单的小类:
public class OldFashionedThread
{
private readonly List<string> _listOfStrings = new List<string>();
public IEnumerable<string> ListOfStrings => _listOfStrings;
public Form1 TheForm { get; set; }
public void DoWork(object state)
{
var strings = new[] {"Some", "strings", "go", "here"};
foreach (var s in strings)
{
_listOfStrings.Add(s);
Thread.Sleep(500);
}
TheForm?.Invoke(new Action(TheForm.AlertAllDone));
}
}
“AlertAllDone”只是弹出一个消息框说“全部完成”
然后,在我的主要表格上,我有:
private OldFashionedThread _worker;
private void StartThreadBtn_Click(object sender, EventArgs e)
{
_worker = new OldFashionedThread {TheForm = this};
ThreadPool.QueueUserWorkItem(_worker.DoWork);
}
private void AllDoneBtn_Click(object sender, EventArgs e)
{
if (_worker != null)
{
var result = string.Join(" ", _worker.ListOfStrings);
MessageBox.Show(result);
}
}
我单击StartThreadBtn按钮,等到 All Done 消息框出现,然后单击AllDoneBtn按钮。
使用任务,我可以做到这一点(直接在表单的代码中):
private async void AsTaskBtn_Click(object sender, EventArgs e)
{
var strings = await GetStringsAsync();
var result = string.Join(" ", strings);
MessageBox.Show(result);
}
private async Task<IEnumerable<string>> GetStringsAsync()
{
return await Task.Run<IEnumerable<string>>(
() => {
var listOfStrings = new List<string>();
var strings = new[] { "Some", "strings", "go", "here" };
foreach (var s in strings)
{
listOfStrings.Add(s);
Thread.Sleep(500);
}
return listOfStrings;
});
}
这await让我等到工作完成(不占用 UI 线程),然后,当它完成时,我醒来并弹出消息框。
利用:
var task = Task.Run<object>(() => /** your delegate returning an object **/)
然后,您可以使用Result、Wait或(最好的、首选的等待)await关键字来处理它。
var returnedObject = await task;
更多关于Task这里:https ://msdn.microsoft.com/en-us/library/system.threading.tasks.task(v=vs.110).aspx
假设getObject(string id)方法是线程安全的并且是 CPU 密集型的(即它不执行 I/O 操作),那么PLINQ可能是最好的选择。它自动执行分区输入列表的所有魔法,将工作负载分配到所有可用的 CPU 内核,然后将子结果合并回一个结果列表。
List<string> ids = new List<string>(new[] { "a", "b", "c", "d", "e", "f" });
List<Object> objects = ids
.AsParallel()
.Select(id => getObject(id))
.ToList();
如果需要,您还可以使用.WithDegreeOfParallelism(),.WithExecutionMode()和.WithMergeOptions()扩展方法对其进行配置(但通常使用默认选项可以很好地工作)。
但是如果getObject()方法是 I/O 绑定的,那么使用Task可能是更好的选择。