No, that's not the way it works. The .NET asynchronous Timer classes are perfectly thread-safe. The problem with thread-safety is that it is not a transitive property, it doesn't make the other code that's executed thread-safe as well. The code that you wrote, not a .NET Framework programmer.
It is the same kind of problem with the very common assumption that Windows UI code is fundamentally thread-unsafe. It is not, the code inside Windows is perfectly thread-safe. The problem is all the code that runs that is not part of Windows and not written by a Microsoft programmer. There's always a lot of that code, triggered by a SendMessage() call. Which runs custom code that a programmer wrote. Or code he didn't write, like a hook installed by some utility. Code that assumes that the program doesn't make it difficult and just executes message handlers on one thread. He usually does, not doing that buys him a lot of trouble.
Same problem with the System.Timers.Timer.Elapsed event and the System.Threading.Timer callback. Programmers make lots of mistakes writing that code. It runs complete asynchronously on an arbitrary threadpool thread, touching any shared variable really does require locking to protect state. Very easy to overlook. And worse, much worse, very easy to get yourself into a pile of trouble when the code runs again, before the previous invocation stopped running. Triggered when the timer interval is too low or the machine is too heavily loaded. Now there are two threads running the same code, that rarely comes to a good end.
Threading is hard, news at eleven.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…