1

我正在制作一个应用程序,由于某种原因,当我单击一个按钮时,我必须启动一个新表单,同时在谷歌文档中创建一个新文档。我已经成功实现了上述内容,但是当应用程序忙于在谷歌文档上创建一个新文档时,新加载的表单的 UI 冻结了。我在某处读到,如果我使用多线程可以避免这种情况。所以现在我想问我是否应该创建两个线程,在其中一个线程中我应该放置用于创建新表单的代码,在另一个线程中我应该放置在 google doc 中创建文档的代码。或者我应该只在线程上创建,我应该在其中放置代码来创建新的谷歌文档,并让新的表单创建代码在主进程中?另外在已经编写的代码中实现线程的最简单方法是什么?如果可能,请提供一些参考阅读材料。

4

5 回答 5

6

你实际上有很多选择。

(1)背景工作者。如果您真的想要在 WinForms 中进行异步工作的最简单的编程模型,那就是这个。它通常用于执行一些异步任务并报告进度,但如果您不需要,则不必报告进度。

(2)基于事件的异步模式。如果您想制作一个完整的组件来执行一些异步任务,完全控制报告进度和您自己的自定义事件,那么这是一种方法。这也比 BackgroundWorker 更能帮助你理解线程。因为我是一个视觉型的人,所以我创建了一个完整的视频演示,介绍如何使用 WinForms 做到这一点

(3)任务并行库。您可以将 TPL 与 WinForms 一起使用,我在此处写了一篇关于如何做到这一点的非常详细的博客文章

(4)异步和等待。请注意,这需要 .NET 4.5、C# 5.0 和仅包含在 Visual Studio 11 中的 C# 5.0 编译器,目前仅在 BETA 中。但是,我也有一篇关于如何做到这一点的完整博客文章

(5)带线程的ISynchronizedInvoke。这是另一种选择,我也有一个关于.

这完全取决于您选择哪种方法。我的建议是简要地看一下每一个,然后根据你对主题的先进程度选择一种方法,或者哪种方法最能满足你的要求。

于 2012-04-19T08:33:29.037 回答
2

您可以使用多种技术来完成您的要求,但我会为此推荐任务并行库 (TPL) (或 a BackgroundWorker)。

创建/启动新表单的开销很小(在大多数情况下),因此我认为您应该在 UI 线程上启动表单,并在后台线程上创建 Google Doc。所以使用 TPL 你会有类似的东西(非常基本的例子)

// In click event.
MyForm newForm = new MyForm();
newForm.Show();

Task googleDocTask = Task.Factory.StartNew(() =>
{
    // Do your stuff with Google Docs.
    // Note you cannot access the UI thread from within this delegate.
});

有关 C# 中线程的精彩讨论,请参阅Joseph Albahari 的线程页面

有关 TPL 的更多信息和相当完整的介绍,请参见此处

我希望这有帮助。

于 2012-04-19T08:22:09.960 回答
2

只需创建一个线程。我建议使用 BackgroundWorker。他们非常直截了当。

把它放在你班级的顶部:

private BackgroundWorker googleDocWorker = new BackgroundWorker();

把它放在你的构造函数中:

googleDocWorker.DoWork += new DoWorkEventHandler(googleDocWorker_DoWork);
googleDocWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(googleDocWorker_RunWorkerCompleted);

将这些方法放在您的类中:

void googleDocWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    // You can use this to alert you that the google doc is created.
}

void googleDocWorker_DoWork(object sender, DoWorkEventArgs e)
{
    // Create google doc here.
}

调用它开始创建谷歌文档:

googleDocWorker.RunWorkerAsync();

现在,如果您需要将一些数据传递到 BackgroundWorker,您可以传递任何您真正想要的数据。您可以使用对象数组传入一个字符串,甚至是多个不同类型的对象。这是一个发送多个对象的示例:

googleDocWorker.RunWorkerAsync(new object[] { "doc name", contents });

现在,这意味着它们必须在 _DoWork 方法中处理:

void googleDocWorker_DoWork(object sender, DoWorkEventArgs e)
{
    // Create google doc here.
    object[] args = (object[])e.Argument;
    String docName = (string)args[0];
    SomeClass contents = (SomeClass)args[1];
}

假设,在创建文档后,您想将 URL 发送回刚刚创建的文档,您只需将其从 _DoWork 方法传回 _RunWorkerCompleted 方法:

void googleDocWorker_DoWork(object sender, DoWorkEventArgs e)
{
    // Create google doc here.

    ...

    e.Result = myURL;
}

在 RunWorkerCompleted 方法中获取 URL,它与 DoWork 方法几乎相同。

void googleDocWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    // You can use this to alert you that the google doc is created.
    String docURL = (String)e.Result;
}

希望有帮助!(:

于 2012-04-19T08:27:27.203 回答
0

恕我直言。您应该只创建一个用于制作 google doc 的线程(因此您将拥有两个线程)。

于 2012-04-19T08:18:44.893 回答
0

最简单的方法是使用 BackgroundWorker 或使用 ThreadPool。如果您的主 UI 不关心其他任务何时完成,线程池会更简单。

于 2012-04-19T08:25:19.217 回答