线程不';t在主线程结束时终止
本文关键字:线程 结束 终止 | 更新日期: 2023-09-27 17:57:26
我有一个奇怪的问题:
在我的C#应用程序中,我正在创建另一个线程,就像这样:
Thread printThread = new Thread(printWorker);
printThread.Name = "Logger MainThread";
printThread.IsBackground = true;
printThread.Start();
当我的主线程完成时,这个新线程继续工作,尽管它被标记为Background。
造成这种情况的原因是什么?此对象持有一个Mutex对象,不确定这可能是原因。。。
有什么想法吗?
以下是printWorker方法的代码:
while (loggerIsActive)
{
LogMessage log = LoggerQueue.Dequeue();
if (log.message != null)
{
syncLogObj.WaitOne();
lock (writerobj)
{
StreamWriter sw;
if (!File.Exists(fName))
{
sw = File.CreateText(fName);
}
else
{
sw = new StreamWriter(fName, true);
}
using (sw)
{
if (log.message != "")
{
if (log.message.EndsWith("'r'n"))
{
log.message =
log.message.Substring(0, log.message.Length - 2);
}
sw.WriteLine(string.Format("[{0}][{3}][{1}] | {2}",
log.msgTime,
log.level.ToString(),
log.message,
log.sender.ToString()));
}
sw.Flush();
sw.Close();
}
}
syncLogObj.ReleaseMutex();
}
Thread.Sleep(5);
}
试试这个:
通过VS启动应用程序并正常退出。VS应保持在调试模式,如您所述。单击暂停按钮(全部中断),然后转到调试->窗口->线程。您在列表中看到您的"Logger MainThread"了吗?
- 如果是这样,双击它,它将引导您进入线程当前正在执行的代码行。从那里开始逐步调试,看看为什么它没有终止
- 如果您没有看到它,请尝试查看其他未终止的线程,并尝试查找问题
否则,对于此类问题,通过System.Diagnostics.Debug.Print语句监视程序状态总是很有用的(您可以在VS输出窗口中看到它们的打印)。
杀死它
不漂亮。但这不是电视。继续阅读:
1) 不确定您是否正在使用它,但似乎应该在排队(主pgm)或出列(线程)之前锁定loggerqueue
2) 无需仅通过此设置锁定writerobj。但实际上你应该这样做,这样你就可以安全地杀死线程,而不是在写:
- 主螺纹:
- 什么都做
- 关闭前:-锁定写robj-打印螺纹。bort
- 工作线程:
- 添加try-catch来处理threadabort异常并立即退出
如果您正确地做到了这一点,就不应该使用Wait和互斥。如果你正确使用wait,你就不需要睡眠了。
这个应用程序的一般建议:为什么不登录主线程?如果您的日志记录如此繁忙,那么日志结果将毫无用处。
但在极少数情况下,这可能是错误的。Enonces。。。。。。
让线程处理这个问题的一般建议:
- 主程序
- 在对象中封装日志记录(特别是退出标志、队列和工作线程引用)
- "全球势利者?"日志记录是使用singleton模式的罕见借口
- 通过方法启动记录器对象中的工作线程
- 主线程总是在logger对象上调用一个方法来记录错误
- 该方法锁定队列并将其添加到队列中
- 使用监视器/脉冲/等待,无睡眠;充分的例子比比皆是;这是值得学习的
- 因为无论如何只有这个线程在处理文件,所以除非有多个进程,否则不需要waitone/releasemutex
- 该日志记录方法监视器对对象进行脉冲
- 这释放了工作线程的monitor.wait(它使CPU空闲而不是睡眠)
- 锁定队列,只在锁定内部将对象出列到本地ref;没有别的
- 执行正常的日志代码和"退出检查"循环。添加
- 若退出时队列已满,则您的逻辑代码可能会使消息不写:
- 更改为退出检查,这样您就可以在不需要额外锁定队列的情况下执行此操作:
- 将队列对象引用的声明移动到while之上;将其设置为零
- 将while中的逻辑更改为'loggerisactive或log!=null'
- 当主线程完成时,在退出代码中:
- 设置退出标志
- 脉冲您用来等待的对象,以防它没有处理队列
- 线会掉下来
你有很多事情显然没有表现出来。。。
Exmaple:您有syncLogObj.WaitOne();
,但我们看不到syncLogObj
在哪里被声明,也看不到在程序的其他地方被使用。
另外,你不需要它…完全去掉syncLogObj
的东西(包括"ReleaseMutex"垃圾)。。。您已经有了一个锁(blah){},这就是您所需要的(根据您显示的代码)。
主线程很可能没有结束,可能是因为这个或其他对象使它保持打开状态。
所以,简单的说明
- 去掉
syncLogObj
(因为你已经有了"锁") - 确保将
loggerIsActive = false
设置在某个位置
编辑:更多细节
据我所见-您根本不需要lock (writerobj)
,因为(我很确定),您似乎只有一个线程在写入日志。
只有当您有两个或多个线程运行该代码(基本上)时,"锁"才存在。
如果printworker
没有在主线程完成之前完成,那么主线程将终止,而printworker
线程将被操作系统终止。如果您希望main等待您创建的线程,那么您应该在main中调用printThread.Join()
。这将使main在您的线程上等待。
当main完成时,您的程序将失效,并且您的printThread
将被操作系统销毁,它将无法继续运行。
从这里
背景线程与前台线程,但有一个例外:后台线程不保留托管执行环境正在运行。一旦所有前台线程在托管进程中停止(其中.exe文件是托管程序集),系统停止所有后台线程并关闭。
Tony the Tiger有正确的想法,但需要添加额外的代码来在应用程序关闭之前杀死线程。
printThread.Join(1000);
if(printThread!=null && printThread.IsAlive)
printThread.Abort();
Thread.Abort();
Thread.Dispose();
如果我没记错的话,那就行了。