每当您创建 WinForm 应用程序时,它都会启动一个新进程并创建一个新线程。对用户界面的任何更新都在与您的进程相同的线程上完成。这意味着当您的应用程序正在执行“繁忙工作”时,您的 UI 将被阻塞,因为它们位于同一线程上。这意味着,为了实现您想要实现的目标,您必须做一些额外的工作。
我们需要做的第一步是为您的工作例程创建一个函数(我们可以使用匿名函数,但由于您是 C# 新手,我认为如果我们将其分解出来会更容易理解),如下所示:
private void DoWork()
{
for (int i = 1; i <= sourceTable.Rows.Count - 1; i++)
{
string checkout;
checkout= sourceTable.Rows[i].Field<string>(0);
dest = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["local"].ConnectionString);
dest.Open();
destcmd = new SqlCommand(checkout, dest);
destcmd.ExecuteNonQuery();
dest.Close();
prcmail();
prcmessagecheck();
lblProgress.Text = "Hello World"+i;
Thread.Sleep(1000); // I changed this from 10000 to 1000 (10 seconds down to 1 second)
}
}
接下来,我们需要创建一个新线程来执行我们的DoWork()
功能。目前还不清楚“触发”是什么来完成你的工作,但我假设它是一个按钮单击:
private void button1_click(object sender, EventArgs e)
{
var work = new Thread(DoWork);
work.Start();
}
所以现在,每当有人单击按钮时,我们都会启动一个新线程来执行我们的DoWork
该线程中的函数。新线程产生,然后立即返回执行,并且我们的 GUI 现在将随着我们的线程在后台执行而实时更新。
可是等等!我们还有一个问题需要解决。问题是 Window 的表单控件不是线程安全的,如果我们尝试从 GUI 线程之外的另一个线程更新控件,我们将收到跨线程操作错误。解决这个问题的关键是使用InvokeRequired
and Invoke
.
首先,我们需要创建另一个仅执行标签更新的函数:
private void SetProgressLabel(int progress)
{
lblProgress.Text = "Hello World" + progress;
}
在您的表单类中,我们还需要创建一个新的委托:
public partial class Form1 : Form
{
private delegate void ProgressCallback(int progress);
// ..
// The rest of your code
// ..
}
最后,改变你的DoWork()
方法如下:
private void DoWork()
{
for (int i = 1; i <= sourceTable.Rows.Count - 1; i++)
{
string checkout;
checkout= sourceTable.Rows[i].Field<string>(0);
dest = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["local"].ConnectionString);
dest.Open();
destcmd = new SqlCommand(checkout, dest);
destcmd.ExecuteNonQuery();
dest.Close();
prcmail();
prcmessagecheck();
if (lblProgress.InvokeRequired)
{
lblProgress.Invoke(new ProgressCallback(SetProgressLabel), new object[] { i });
}
else
{
SetProgressLabel(i);
}
Thread.Sleep(1000); // I changed this from 10000 to 1000 (10 seconds down to 1 second)
}
}
这使用标签的(源自Control
) InvokeRequired
属性来确定是否Invoke
是必须的。它返回true
or false
。如果它是false
,我们可以调用我们的SetProgressLabel()
就像我们通常做的那样。如果它是true
,我们必须使用Invoke
来调用我们的函数。
恭喜!您刚刚制作了第一个线程安全应用程序。
现在,顺便说一句,您没有正确释放和处置您的物品。我建议你改变你的DoWork()
代码如下:
private void DoWork()
{
for (int i = 1; i <= sourceTable.Rows.Count - 1; i++)
{
string checkout;
checkout = sourceTable.Rows[i].Field<string>(0);
using (dest = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["local"].ConnectionString))
{
dest.Open();
using (destcmd = new SqlCommand(checkout, dest))
{
destcmd.ExecuteNonQuery();
dest.Close();
prcmail();
prcmessagecheck();
if (lblProgress.InvokeRequired)
{
lblProgress.Invoke(new ProgressCallback(SetProgressLabel), new object[] { i });
}
else
{
SetProgressLabel(i);
}
Thread.Sleep(1000); // I changed this from 10000 to 1000 (10 seconds down to 1 second)
}
}
}
}
因为我包裹了你IDisposable
's into using
块,一旦超出范围,资源将自动被处置。