我目前正在开发一个通过IMAP检查来自电子邮件帐户的电子邮件的应用程序。此功能每5秒调用一次,需要一些时间才能完成。
private void CheckForRequests()
{
List<string[]> mails = CollectAllMails();
for (int i = 0; i < mails.Count; i++)
{
if (mails[i][0].Split('_')[0] == "request")
{
//INVITATION TO ME
if (mails[i][0].Split('_')[2] == username && mails[i][0].Split('_')[3] == "request")
{
DeleteMail(mails[i][0]);
MessageBoxResult result = MessageBox.Show("Do you accept the request from " + mails[i][0].Split('_')[1], "Invitation", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes);
if (result == MessageBoxResult.Yes)
{
DeleteMail("request_" + mails[i][0].Split('_')[1] + "_" + mails[i][0].Split('_')[2] + "_" + mails[i][0].Split('_')[3]);
SendMail("request_" + mails[i][0].Split('_')[1] + "_" + mails[i][0].Split('_')[2] + "_accept", "");
ChatWindow chat = new ChatWindow();
chat.ShowDialog();
//do open chat window
}
else if (result == MessageBoxResult.No)
{
DeleteMail("request_" + mails[i][0].Split('_')[1] + mails[i][0].Split('_')[2]);
SendMail("request_" + mails[i][0].Split('_')[1] + "_" + mails[i][0].Split('_')[2] + "_decline", "");
}
}
//ACCEPTION FROM ANOTHER DUDE
else if (mails[i][0].Split('_')[2] != username && mails[i][0].Split('_')[3] == "accept")
{
ChatWindow chat = new ChatWindow();
chat.ShowDialog();
}
//REJECTION FROM ANOTHER DUDE
else if (mails[i][0].Split('_')[2] != username && mails[i][0].Split('_')[3] == "decline")
{
MessageBox.Show("Your invitation was declined.", "Sorry", MessageBoxButton.OK, MessageBoxImage.Exclamation);
}
}
else if (mails[i][0].Split('_')[0] == "somethingelse")
{
}
}
}
我的循环每隔5秒调用一次这个方法,在这段时间里我无法在我的应用程序中编写或执行任何操作。我很确定我必须使用Thread
或Task
来解决问题,但我没有发现如何解决这个问题。当我在Task
中调用该方法时,我单击是它会崩溃并说它必须是STA-Thread
...在这种情况下我甚至不想访问GUI线程,我只想检查邮件,如果方法发现了什么,它应该做什么,比如从任务中断并调用方法(不是来自任务)。
这个问题最干净的解决方案是什么?
答案 0 :(得分:3)
您的线程问题是由您尝试在非UI线程上执行UI操作引起的。每当您调用此类UI内容时,您都可以使用Dispatcher.Invoke解决此问题
Sheets("Dinamicos").Range(Cells(45, W), Cells(46, W1))
所以在你的情况下,你有类似的东西
Application.Current.Dispatcher.Invoke(() =>
{
// Your stuff here
});
答案 1 :(得分:1)
你对需要使用线程是正确的
虽然@Gareth关于是否需要使用调度程序来正确访问各个元素,但我实际上并没有在代码中看到任何线程,尽管错误消息清楚地证明您已经尝试了一些。
实现线程,你有各种选择
首先,您可以通过Tasks或较旧的Thread类直接执行此操作
这样就可以这样做了
private void CheckForRequestsAsync()=> Task.Run(()=>CheckForRequests());
这将立即创建并启动一个任务,该任务将在一个单独的线程中执行CheckForRequests,释放GUI以继续其在GUI上的工作,但这是一个非常基本的实现,可能需要进一步增强才能可靠地满足您的需求
另一种选择是利用.Net中的一些较新功能并使用async关键字
如果您将CheckForRequests声明为private async void CheckForRequests (object sender, EventArgs e)
,那么void将自动转换为一个任务,可以由事件处理程序触发,作为异步任务说明Timer
例如
Timer timer = new Timer(5000);
timer.Elapsed += CheckForRequests; //where CheckForRequests has the async keyword
timer.Start();
将此与@Gareth建议的调度程序信息结合起来,这些信息会引发跨线程访问异常并且您应该准备好了
这看起来像这样:
MessageBoxResult result = Dispatcher.Invoke(() =>
MessageBox.Show("Do you accept the request from " + mails[i][0].Split('_')[1], "Invitation", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes);
);
请注意,如果您使用async
而不使用await
关键字,那么您将收到线程可能在任何工作线程完成之前退出的警告,但这只是一个警告,就好像您不是t调用方法中的任何工作线程,或者在退出之前不需要它们完成,那么就没有任何损害
最后 其中一条评论建议使用DispatcherTimer而不是Timer,我不建议这样做,因为每次计时器滴答时,它都会在GUI线程中运行你的代码,就像你已经看到的那样锁定它,DispatcherTimer最好用于定时器严重改变GUI并快速运行
虽然如果你重新定义了代码,那么你可以通过从慢速运行的进程中分解gui元素来使用DispatcherTimer
dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
dispatcherTimer.Tick += (s,e)=>{
if( MessageBox.Show("Do you accept the request", "Invitation", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes) == MessageBoxResult.Yes)
{
Task.Run(()=>CheckForRequests());
}
}
dispatcherTimer.Interval = new TimeSpan(0,0,1);
dispatcherTimer.Start();