SQL 执行时动画进度条
Animate Progress Bar while SQL Executes
在我的 C# WindowsForm 应用程序中,我试图使用固定的持续时间(在我的例子中为 6500 毫秒)从头到尾为进度条制作动画。这大致是 SQL 执行所需的时间。
我已经尝试 运行 在执行 SQL 之前启用 animate 方法,但是当 SQL 正在执行时,似乎整个线程都被冻结了。我不想 运行 异步 SQL 以防出现异常。
我是多线程和后台工作进程的新手,虽然我相信这可能是关键?
如果有人能引导我朝着正确的方向前进,将不胜感激!
private void btnInternalMovement_Click(object sender, EventArgs e)
{
AnimateProgBar(6500);
// ***** BLAH MORE CODE HERE ******
using (SqlCommand cmd = new SqlCommand("CatamacInternalStockMovement_C", SqlConnection))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("@ItemName", items);
cmd.Parameters.AddWithValue("@Quantity", quantities);
cmd.Parameters.AddWithValue("@WarehouseFrom", cbxFromWarehouse.SelectedValue.ToString());
cmd.Parameters.AddWithValue("@WarehouseTo", cbxToWarehouse.SelectedValue.ToString());
cmd.Parameters.AddWithValue("@LocationFrom", cbxFromZone.SelectedValue.ToString());
cmd.Parameters.AddWithValue("@LocationTo", cbxToZone.SelectedValue.ToString());
cmd.Parameters.AddWithValue("@UOM", "Each");
cmd.Parameters.AddWithValue("@TransferDate", DateTime.Now);
cmd.Parameters.AddWithValue("@User", User);
cmd.ExecuteNonQuery();
}
// PROGRESS BAR ONLY STARTS ANIMATING HERE
MessageBox.Show("Success!");
}
这里是定时器/动画方法的代码。
private void timer1_Tick(object sender, EventArgs e)
{
if (progressBar1.Value < 100)
{
progressBar1.Value += 1;
progressBar1.Refresh();
}
else
{
timer1.Enabled = false;
}
}
public void AnimateProgBar(int milliSeconds)
{
if (timer1.Enabled) return;
progressBar1.Value = 0;
timer1.Interval = milliSeconds / 100;
timer1.Enabled = true;
}
您的代码中可能存在一些错误;
你似乎没有 Start() 你的计时器,它可能应该放在 AnimateProgBar 中。
AnimateProgBar 在它的参数中期望毫秒,但你这样做了
timer1.Interval = 毫秒/100;
这是错误的,因为 .Interval 也需要毫秒。
要回答您的问题,只需将 btnInternalMovement_Click 更改为
private async void btnInternalMovement_Click(object sender, EventArgs e)
{
AnimateProgBar(6500);
// ***** BLAH MORE CODE HERE ******
await Task.Run(() => {
using (SqlCommand cmd = new SqlCommand("CatamacInternalStockMovement_C", SqlConnection))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("@ItemName", items);
cmd.Parameters.AddWithValue("@Quantity", quantities);
cmd.Parameters.AddWithValue("@WarehouseFrom", cbxFromWarehouse.SelectedValue.ToString());
cmd.Parameters.AddWithValue("@WarehouseTo", cbxToWarehouse.SelectedValue.ToString());
cmd.Parameters.AddWithValue("@LocationFrom", cbxFromZone.SelectedValue.ToString());
cmd.Parameters.AddWithValue("@LocationTo", cbxToZone.SelectedValue.ToString());
cmd.Parameters.AddWithValue("@UOM", "Each");
cmd.Parameters.AddWithValue("@TransferDate", DateTime.Now);
cmd.Parameters.AddWithValue("@User", User);
cmd.ExecuteNonQuery();
}
// PROGRESS BAR ONLY STARTS ANIMATING HERE
MessageBox.Show("Success!");
});
}
请注意,该方法现在标记为异步,长 运行 内容位于 Task.Run 中(将其置于后台)。您真的不希望在 UI 线程中发生长时间的 运行 任务。
显然,异步编程的内容比我在这个答案中提出的要多得多,所以请参阅 https://msdn.microsoft.com/en-us/library/mt674882.aspx
因为 cmd.ExecuteNonQuery();
在 btnInternalMovement_Click
的回调中执行,它在 UI 线程上执行。 UI 将在完成任何回调(包括 btnInternalMovement_Click
)期间被阻塞。如果 cmd.ExecuteNonQuery()
需要很长时间才能完成,那么您会在 UI 中看到更新冻结;键盘和鼠标输入无响应。
async/await 与 Network-bound 通话
你对数据库的调用是 I/O-bound 而不是 CPU-bound 所以你 应该不是 按照 使用 Task.Run()
。如果你想使用 async/await
确保你使用 IOCP 形式而不是旋转 and/or 浪费一个 unnessary worker thread through-out称呼。
避免使用 Task.Run()
,因为 ExecuteNonQueryAsync()
非常好。 ExecuteNonQueryAsync()
是在考虑 IOCP 的情况下构建的,比执行这样的操作更有效:
await Task.Run(() =>
{
// I/O-bound: bad! DON'T DO THIS for I/O-bound operations such
//as disk; network; DB etc
});
改变这个:
private void btnInternalMovement_Click(object sender, EventArgs e)
{
AnimateProgBar(6500);
// ***** BLAH MORE CODE HERE ******
using (SqlCommand cmd = new SqlCommand("CatamacInternalStockMovement_C", SqlConnection))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("@ItemName", items);
cmd.Parameters.AddWithValue("@Quantity", quantities);
cmd.Parameters.AddWithValue("@WarehouseFrom", cbxFromWarehouse.SelectedValue.ToString());
cmd.Parameters.AddWithValue("@WarehouseTo", cbxToWarehouse.SelectedValue.ToString());
cmd.Parameters.AddWithValue("@LocationFrom", cbxFromZone.SelectedValue.ToString());
cmd.Parameters.AddWithValue("@LocationTo", cbxToZone.SelectedValue.ToString());
cmd.Parameters.AddWithValue("@UOM", "Each");
cmd.Parameters.AddWithValue("@TransferDate", DateTime.Now);
cmd.Parameters.AddWithValue("@User", User);
cmd.ExecuteNonQuery();
}
// PROGRESS BAR ONLY STARTS ANIMATING HERE
MessageBox.Show("Success!");
}
...至:
private async void btnInternalMovement_Click(object sender, EventArgs e)
{
AnimateProgBar(6500);
// ***** BLAH MORE CODE HERE ******
using (SqlCommand cmd = new SqlCommand("CatamacInternalStockMovement_C", SqlConnection))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("@ItemName", items);
cmd.Parameters.AddWithValue("@Quantity", quantities);
cmd.Parameters.AddWithValue("@WarehouseFrom", cbxFromWarehouse.SelectedValue.ToString());
cmd.Parameters.AddWithValue("@WarehouseTo", cbxToWarehouse.SelectedValue.ToString());
cmd.Parameters.AddWithValue("@LocationFrom", cbxFromZone.SelectedValue.ToString());
cmd.Parameters.AddWithValue("@LocationTo", cbxToZone.SelectedValue.ToString());
cmd.Parameters.AddWithValue("@UOM", "Each");
cmd.Parameters.AddWithValue("@TransferDate", DateTime.Now);
cmd.Parameters.AddWithValue("@User", User);
await cmd.ExecuteNonQueryAsync(); // <----- NEW
}
// PROGRESS BAR ONLY STARTS ANIMATING HERE
MessageBox.Show("Success!");
}
另见
- When correctly use Task.Run and when just async-await
在我的 C# WindowsForm 应用程序中,我试图使用固定的持续时间(在我的例子中为 6500 毫秒)从头到尾为进度条制作动画。这大致是 SQL 执行所需的时间。
我已经尝试 运行 在执行 SQL 之前启用 animate 方法,但是当 SQL 正在执行时,似乎整个线程都被冻结了。我不想 运行 异步 SQL 以防出现异常。
我是多线程和后台工作进程的新手,虽然我相信这可能是关键?
如果有人能引导我朝着正确的方向前进,将不胜感激!
private void btnInternalMovement_Click(object sender, EventArgs e)
{
AnimateProgBar(6500);
// ***** BLAH MORE CODE HERE ******
using (SqlCommand cmd = new SqlCommand("CatamacInternalStockMovement_C", SqlConnection))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("@ItemName", items);
cmd.Parameters.AddWithValue("@Quantity", quantities);
cmd.Parameters.AddWithValue("@WarehouseFrom", cbxFromWarehouse.SelectedValue.ToString());
cmd.Parameters.AddWithValue("@WarehouseTo", cbxToWarehouse.SelectedValue.ToString());
cmd.Parameters.AddWithValue("@LocationFrom", cbxFromZone.SelectedValue.ToString());
cmd.Parameters.AddWithValue("@LocationTo", cbxToZone.SelectedValue.ToString());
cmd.Parameters.AddWithValue("@UOM", "Each");
cmd.Parameters.AddWithValue("@TransferDate", DateTime.Now);
cmd.Parameters.AddWithValue("@User", User);
cmd.ExecuteNonQuery();
}
// PROGRESS BAR ONLY STARTS ANIMATING HERE
MessageBox.Show("Success!");
}
这里是定时器/动画方法的代码。
private void timer1_Tick(object sender, EventArgs e)
{
if (progressBar1.Value < 100)
{
progressBar1.Value += 1;
progressBar1.Refresh();
}
else
{
timer1.Enabled = false;
}
}
public void AnimateProgBar(int milliSeconds)
{
if (timer1.Enabled) return;
progressBar1.Value = 0;
timer1.Interval = milliSeconds / 100;
timer1.Enabled = true;
}
您的代码中可能存在一些错误;
你似乎没有 Start() 你的计时器,它可能应该放在 AnimateProgBar 中。
AnimateProgBar 在它的参数中期望毫秒,但你这样做了
timer1.Interval = 毫秒/100;
这是错误的,因为 .Interval 也需要毫秒。
要回答您的问题,只需将 btnInternalMovement_Click 更改为
private async void btnInternalMovement_Click(object sender, EventArgs e)
{
AnimateProgBar(6500);
// ***** BLAH MORE CODE HERE ******
await Task.Run(() => {
using (SqlCommand cmd = new SqlCommand("CatamacInternalStockMovement_C", SqlConnection))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("@ItemName", items);
cmd.Parameters.AddWithValue("@Quantity", quantities);
cmd.Parameters.AddWithValue("@WarehouseFrom", cbxFromWarehouse.SelectedValue.ToString());
cmd.Parameters.AddWithValue("@WarehouseTo", cbxToWarehouse.SelectedValue.ToString());
cmd.Parameters.AddWithValue("@LocationFrom", cbxFromZone.SelectedValue.ToString());
cmd.Parameters.AddWithValue("@LocationTo", cbxToZone.SelectedValue.ToString());
cmd.Parameters.AddWithValue("@UOM", "Each");
cmd.Parameters.AddWithValue("@TransferDate", DateTime.Now);
cmd.Parameters.AddWithValue("@User", User);
cmd.ExecuteNonQuery();
}
// PROGRESS BAR ONLY STARTS ANIMATING HERE
MessageBox.Show("Success!");
});
}
请注意,该方法现在标记为异步,长 运行 内容位于 Task.Run 中(将其置于后台)。您真的不希望在 UI 线程中发生长时间的 运行 任务。
显然,异步编程的内容比我在这个答案中提出的要多得多,所以请参阅 https://msdn.microsoft.com/en-us/library/mt674882.aspx
因为 cmd.ExecuteNonQuery();
在 btnInternalMovement_Click
的回调中执行,它在 UI 线程上执行。 UI 将在完成任何回调(包括 btnInternalMovement_Click
)期间被阻塞。如果 cmd.ExecuteNonQuery()
需要很长时间才能完成,那么您会在 UI 中看到更新冻结;键盘和鼠标输入无响应。
async/await 与 Network-bound 通话
你对数据库的调用是 I/O-bound 而不是 CPU-bound 所以你 应该不是 按照 Task.Run()
。如果你想使用 async/await
确保你使用 IOCP 形式而不是旋转 and/or 浪费一个 unnessary worker thread through-out称呼。
避免使用 Task.Run()
,因为 ExecuteNonQueryAsync()
非常好。 ExecuteNonQueryAsync()
是在考虑 IOCP 的情况下构建的,比执行这样的操作更有效:
await Task.Run(() =>
{
// I/O-bound: bad! DON'T DO THIS for I/O-bound operations such
//as disk; network; DB etc
});
改变这个:
private void btnInternalMovement_Click(object sender, EventArgs e)
{
AnimateProgBar(6500);
// ***** BLAH MORE CODE HERE ******
using (SqlCommand cmd = new SqlCommand("CatamacInternalStockMovement_C", SqlConnection))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("@ItemName", items);
cmd.Parameters.AddWithValue("@Quantity", quantities);
cmd.Parameters.AddWithValue("@WarehouseFrom", cbxFromWarehouse.SelectedValue.ToString());
cmd.Parameters.AddWithValue("@WarehouseTo", cbxToWarehouse.SelectedValue.ToString());
cmd.Parameters.AddWithValue("@LocationFrom", cbxFromZone.SelectedValue.ToString());
cmd.Parameters.AddWithValue("@LocationTo", cbxToZone.SelectedValue.ToString());
cmd.Parameters.AddWithValue("@UOM", "Each");
cmd.Parameters.AddWithValue("@TransferDate", DateTime.Now);
cmd.Parameters.AddWithValue("@User", User);
cmd.ExecuteNonQuery();
}
// PROGRESS BAR ONLY STARTS ANIMATING HERE
MessageBox.Show("Success!");
}
...至:
private async void btnInternalMovement_Click(object sender, EventArgs e)
{
AnimateProgBar(6500);
// ***** BLAH MORE CODE HERE ******
using (SqlCommand cmd = new SqlCommand("CatamacInternalStockMovement_C", SqlConnection))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("@ItemName", items);
cmd.Parameters.AddWithValue("@Quantity", quantities);
cmd.Parameters.AddWithValue("@WarehouseFrom", cbxFromWarehouse.SelectedValue.ToString());
cmd.Parameters.AddWithValue("@WarehouseTo", cbxToWarehouse.SelectedValue.ToString());
cmd.Parameters.AddWithValue("@LocationFrom", cbxFromZone.SelectedValue.ToString());
cmd.Parameters.AddWithValue("@LocationTo", cbxToZone.SelectedValue.ToString());
cmd.Parameters.AddWithValue("@UOM", "Each");
cmd.Parameters.AddWithValue("@TransferDate", DateTime.Now);
cmd.Parameters.AddWithValue("@User", User);
await cmd.ExecuteNonQueryAsync(); // <----- NEW
}
// PROGRESS BAR ONLY STARTS ANIMATING HERE
MessageBox.Show("Success!");
}
另见
- When correctly use Task.Run and when just async-await