sách gpt4 ai đã đi

Tại sao System.Timers.Timer tồn tại được sau GC nhưng System.Threading.Timer thì không?

In lại 作者:行者123 更新时间:2023-12-03 05:24:26 29 4
mua khóa gpt4 Nike

看来 Hệ thống.Bộ đếm thời gian.Bộ đếm thời gian 实例通过某种机制保持事件状态,但 Hệ thống.Threading.Timer 实例则不然。

示例程序,具有定期Hệ thống.Threading.Timer和自动重置Hệ thống.Bộ đếm thời gian.Bộ đếm thời gian:

class Program
{
void tĩnh Main(string[] args)
{
var timer1 = new System.Threading.Timer(
_ => Console.WriteLine("Stayin alive (1)..."),
null,
0,
400);

var timer2 = new System.Timers.Timer
{
Interval = 400,
AutoReset = true
};
timer2.Elapsed += (_, __) => Console.WriteLine("Stayin alive (2)...");
timer2.Enabled = true;

System.Threading.Thread.Sleep(2000);

Console.WriteLine("Invoking GC.Collect...");
GC.Collect();

Console.ReadKey();
}
}

当我运行此程序(.NET 4.0 客户端、Release、调试器外部)时,只有 Hệ thống.Threading.Timer 被 GC 处理:

Stayin alive (1)...
Stayin alive (1)...
Stayin alive (2)...
Stayin alive (1)...
Stayin alive (2)...
Stayin alive (1)...
Stayin alive (2)...
Stayin alive (1)...
Stayin alive (2)...
Invoking GC.Collect...
Stayin alive (2)...
Stayin alive (2)...
Stayin alive (2)...
Stayin alive (2)...
Stayin alive (2)...
Stayin alive (2)...
Stayin alive (2)...
Stayin alive (2)...
Stayin alive (2)...

biên tập:我已经接受了下面约翰的回答,但我想稍微解释一下。

运行上面的示例程序时(在 Sleep 处设置断点),以下是相关对象和 GCHandle 表的状态:

!dso
OS Thread Id: 0x838 (2104)
ESP/REG Object Name
0012F03C 00c2bee4 System.Object[] (System.String[])
0012F040 00c2bfb0 System.Timers.Timer
0012F17C 00c2bee4 System.Object[] (System.String[])
0012F184 00c2c034 System.Threading.Timer
0012F3A8 00c2bf30 System.Threading.TimerCallback
0012F3AC 00c2c008 System.Timers.ElapsedEventHandler
0012F3BC 00c2bfb0 System.Timers.Timer
0012F3C0 00c2bfb0 System.Timers.Timer
0012F3C4 00c2bfb0 System.Timers.Timer
0012F3C8 00c2bf50 System.Threading.Timer
0012F3CC 00c2bfb0 System.Timers.Timer
0012F3D0 00c2bfb0 System.Timers.Timer
0012F3D4 00c2bf50 System.Threading.Timer
0012F3D8 00c2bee4 System.Object[] (System.String[])
0012F4C4 00c2bee4 System.Object[] (System.String[])
0012F66C 00c2bee4 System.Object[] (System.String[])
0012F6A0 00c2bee4 System.Object[] (System.String[])

!gcroot -nostacks 00c2bf50

!gcroot -nostacks 00c2c034
DOMAIN(0015DC38):HANDLE(Strong):9911c0:Root: 00c2c05c(System.Threading._TimerCallback)->
00c2bfe8(System.Threading.TimerCallback)->
00c2bfb0(System.Timers.Timer)->
00c2c034(System.Threading.Timer)

!gchandles
GC Handle Statistics:
Strong Handles: 22
Pinned Handles: 5
Async Pinned Handles: 0
Ref Count Handles: 0
Weak Long Handles: 0
Weak Short Handles: 0
Other Handles: 0
Statistics:
MT Count TotalSize Class Name
7aa132b4 1 12 System.Diagnostics.TraceListenerCollection
79b9f720 1 12 System.Object
79ba1c50 1 28 System.SharedStatics
79ba37a8 1 36 System.Security.PermissionSet
79baa940 2 40 System.Threading._TimerCallback
79b9ff20 1 84 System.ExecutionEngineException
79b9fed4 1 84 System.StackOverflowException
79b9fe88 1 84 System.OutOfMemoryException
79b9fd44 1 84 System.Exception
7aa131b0 2 96 System.Diagnostics.DefaultTraceListener
79ba1000 1 112 System.AppDomain
79ba0104 3 144 System.Threading.Thread
79b9ff6c 2 168 System.Threading.ThreadAbortException
79b56d60 9 17128 System.Object[]
Total 27 objects

正如 John 在他的回答中指出的那样,两个计时器都在 GCHandle 表中注册了它们的回调 (System.Threading._TimerCallback)。正如 Hans 在评论中指出的那样,完成此操作后,state 参数也保持事件状态。

正如 John 指出的,Hệ thống.Bộ đếm thời gian.Bộ đếm thời gian 保持事件状态是因为它被回调引用(它作为 state 参数传递给内部Hệ thống.Threading.Timer);同样,我们的 System.Threading.Timer 被 GC 回收的原因是因为它的回调没有引用它。

添加对 timer1 回调的显式引用(例如,Console.WriteLine("Stayin alive ("+ timer1.GetType().FullName + ")")) 足以防止 GC。

hiện hữuHệ thống.Threading.Timer上使用单参数构造函数也可以,因为计时器将引用自身作为state参数。以下代码在 GC 之后使两个计时器保持事件状态,因为它们均由 GCHandle 表中的回调引用:

class Program
{
void tĩnh Main(string[] args)
{
System.Threading.Timer timer1 = null;
timer1 = new System.Threading.Timer(_ => Console.WriteLine("Stayin alive (1)..."));
timer1.Change(0, 400);

var timer2 = new System.Timers.Timer
{
Interval = 400,
AutoReset = true
};
timer2.Elapsed += (_, __) => Console.WriteLine("Stayin alive (2)...");
timer2.Enabled = true;

System.Threading.Thread.Sleep(2000);

Console.WriteLine("Invoking GC.Collect...");
GC.Collect();

Console.ReadKey();
}
}

1 Câu trả lời

您可以使用 Windbg、sos 和 !gcroot 回答此问题及类似问题。

0:008> !gcroot -nostacks 0000000002354160
DOMAIN(00000000002FE6A0):HANDLE(Strong):241320:Root:00000000023541a8(System.Thre
ading._TimerCallback)->
00000000023540c8(System.Threading.TimerCallback)->
0000000002354050(System.Timers.Timer)->
0000000002354160(System.Threading.Timer)
0:008>

在这两种情况下, native 计时器都必须阻止回调对象的 GC(通过 GCHandle)。不同之处在于 Hệ thống.Bộ đếm thời gian.Bộ đếm thời gian 的情况回调引用 Hệ thống.Bộ đếm thời gian.Bộ đếm thời gian对象(使用 Hệ thống.Threading.Timer 在内部实现)

关于.net - 为什么 System.Timers.Timer 能在 GC 中幸存下来,而 System.Threading.Timer 却不能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4962172/

29 4 0
Bài viết được đề xuất: database-design - 支持应用程序中自定义字段的设计模式有哪些?
Bài viết được đề xuất: Delphi Chromium 本地存储
Bài viết được đề xuất: 从 data.table 中删除多列
Bài viết được đề xuất: xcode - 为什么我的 Xcode 项目中有一些蓝色文件夹?
行者123
Hồ sơ cá nhân

Tôi là một lập trình viên xuất sắc, rất giỏi!

Nhận phiếu giảm giá Didi Taxi miễn phí
Mã giảm giá Didi Taxi
Giấy chứng nhận ICP Bắc Kinh số 000000
Hợp tác quảng cáo: 1813099741@qq.com 6ren.com