在我的C#WindowsForm应用程序中,我尝试使用固定的持续时间(在我的情况下为6500毫秒)从开始到结束为进度条设置动画。这大概是SQL Execute需要多长时间。
我已经尝试在执行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!");
}
这是timer / Animate方法的代码。
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;
}
答案 0 :(得分:2)
因为cmd.ExecuteNonQuery();
在回调到btnInternalMovement_Click
内部执行,所以它在UI线程上执行。在完成任何回调所需的时间内,UI将被阻止,包括btnInternalMovement_Click
。如果cmd.ExecuteNonQuery()
需要很长时间才能完成,那么您会看到在UI中反映出冻结更新;没有响应的键盘和鼠标输入。
您对数据库的调用是 I / O绑定而非 CPU绑定,因此您不应使用Task.Run()
根据{{3}}。如果您想使用async/await
,请确保使用IOCP表格,而不是在整个通话期间启动和/或浪费一致的工作线程。
当有完美的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!");
}
答案 1 :(得分:1)
您的代码中可能存在一些错误;
你似乎没有开始()你的计时器,它可能应该放在AnimateProgBar中。
AnimateProgBar在它的参数中需要毫秒,但你这样做
timer1.Interval = milliSeconds / 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