我在WinForms应用程序中有两个计时器(System.Windows.Forms.Timer
)。
两个计时器同时启动。一个计时器在程序更新三个标签的整个生命周期中启动和停止,另一个计时器只在每次tick事件更新一个标签时运行和完成其工作。然而,当第一个计时器在其tick事件中运行代码时,第二个计时器不运行。
在第一个计时器tick事件代码中,我插入了多个System.Threading.Thread.Yield();
语句,但是第二个计时器仍然被阻塞,搜索结果为空。
我试着为第二个计时器使用系统线程,但它什么也没做。
我不知道。有什么主意吗?
public partial class fMain2 : Form
{
private System.Windows.Forms.Timer timer;
private System.Windows.Forms.Timer timer2;
private Thread tThread;
private int totTime;
private int curTime;
private int exTime = 0;
public int runTime = 0;
private void cmdRun_Click(object sender, EventArgs e)
{
//calculate total time
totTime = iCount * iDuration;
lblTotTime.Text = totTime.ToString();
lblTotEx.Text = exTime.ToString();
System.Threading.Thread.Yield();
curTime = int.Parse("0" + txtDuration.Text);
System.Threading.Thread.Yield();
this.Refresh();
strFile = "Begin" + ".wav";
snd.SoundLocation = strSoundFilePath + strFile;
snd.PlaySync();
//select first item in the listview
lvTimer.Items[0].Selected = true;
lvi = lvTimer.Items[0];
lvTimer.Refresh();
strFile = lvi.SubItems[1].Text + ".wav";
snd.SoundLocation = strSoundFilePath + strFile;
snd.PlaySync();
System.Threading.Thread.Yield();
strFile = "Go" + ".wav";
snd.SoundLocation = strSoundFilePath + strFile;
snd.PlaySync();
//attempted using a thread for timer2
timer.Start();
//tThread = new Thread(new ThreadStart(timer2.Start));
timer2.Start();
//tThread.Start();
}
private void timerTick(object sender, EventArgs e)
{
string strFile;
curTime -= 1;
totTime -= 1;
exTime += 1;
System.Threading.Thread.Yield();
lblCurTime.Text = curTime.ToString();
lblTotTime.Text = totTime.ToString();
lblTotEx.Text = exTime.ToString();
this.Refresh();
System.Threading.Thread.Yield();
if (curTime == 0)
{
timer.Stop();
System.Threading.Thread.Yield();
System.Threading.Thread.Yield();
strFile = "Stop" + ".wav";
snd.SoundLocation = strSoundFilePath + strFile;
snd.PlaySync();
System.Threading.Thread.Yield();
System.Threading.Thread.Yield();
if (totTime == 0)
{
//this marks the end of the program
timer2.Stop();
//tThread.Abort();
//more code but not relevant
return;
}
else
{ //we are still working down the listview
try
{
lvi = lvTimer.Items[lvi.Index + 1];
lvTimer.Items[lvi.Index].Selected = true;
lvTimer.FocusedItem = lvTimer.Items[lvi.Index];
lvTimer.Refresh();
System.Threading.Thread.Yield();
System.Threading.Thread.Yield();
System.Threading.Thread.Yield();
}
catch (IndexOutOfRangeException ei)
{
strFile = "End" + ".wav";
snd.SoundLocation = strSoundFilePath + strFile;
snd.PlaySync();
bRunning = false;
ResetTime();
return;
}
curTime = int.Parse("0" + txtDuration.Text);
lblCurTime.Text = curTime.ToString();
lblTotTime.Text = totTime.ToString();
System.Threading.Thread.Yield();
System.Threading.Thread.Yield();
//I'm wondering if the soundplayer is causing the problem
strFile = lvi.SubItems[1].Text + ".wav";
snd.SoundLocation = strSoundFilePath + strFile;
System.Threading.Thread.Yield();
System.Threading.Thread.Yield();
snd.PlaySync();
System.Threading.Thread.Yield();
System.Threading.Thread.Yield();
strFile = "Go" + ".wav";
snd.SoundLocation = strSoundFilePath + strFile;
snd.PlaySync();
System.Threading.Thread.Yield();
System.Threading.Thread.Yield();
System.Threading.Thread.Yield();
timer.Start();
}
}
}
private void timer2Tick(object sender, EventArgs e)
{
//this is all timer2 does. It runs as long as the
// program is running.
runTime += 1;
lblTotTotal.Text = (runTime / 60).ToString()
+ ":" + (runTime % 60).ToString("00");
}
}
我正在使用VS 2017。
2条答案
按热度按时间5kgi1eie1#
System.Windows.Forms.Timer
设计用于在UI线程上运行代码。调用System.Threading.Thread.Yield()
会告诉系统运行另一个已准备好在当前内核上运行的线程,但您的计时器希望在UI线程上运行,因此它在任何方面都没有帮助。在我看来,您正在阻止UI线程使用
.PlaySync()
播放您的声音。我建议您将声音的播放推送到后台线程,以释放UI。
根据我从你的代码中收集到的信息,你正在尝试播放一系列类似这样的声音:
但是,如果计时器超时,则取消播放这些声音,并播放
"Stop.wav"
声音。这个结构可以用
Queue<Queue<string>>
建模,您只需要执行嵌套的出队操作并支付所有声音。下面是将队列出队的代码:
请注意,这些都是在
Task.Run
中运行的,因此不会阻塞UI线程。要停止处理声音,只需调用
cts.Cancel();
。现在只需要在
cmdRun_Click
方法中构建队列。您仍然需要一个计时器来确定是否应该调用
cts.Cancel()
。我在发布之前测试了我的
Task.Run
代码。它工作正常。mccptt672#
我的建议是使用async/await。
**步骤1:**删除所有
System.Threading.Thread.Yield();
调用。**步骤2:**删除所有
.Refresh();
调用。**步骤3:**将
timerTick
async
:private async void timerTick(object sender, EventArgs e)
**步骤4:**将每次出现的
snd.PlaySync();
替换为await Task.Run(() => snd.PlaySync());
Task.Run
方法调用ThreadPool
上指定的lambda。await
允许当前线程在lambda在ThreadPool
上运行时继续执行其他操作,如响应用户输入。lambda完成后,当前线程继续执行await
之后的代码,直到下一个await
。或者直到处理程序结束。完成这些更改后,UI应再次响应,并且其他计时器应每秒正常计时。
请注意,通过将
timerTick
处理程序设置为async
,现在可以以重叠的方式调用它,您可以通过检查和更新表单的字段来防止重叠。