C#利用System.Threading.Timer实现多线程中遇到的问题,请求帮助!

时间:2020-12-07 20:40:46
源代码如下:
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            TestClass t = new TestClass();
            t.Start();
            Thread.Sleep(30000);
            t.Dispose();
            Thread.Sleep(30000);
        }
    }

    public class TestClass : IDisposable
    {
        private Timer _timer;

        public void Start()
        {
            _timer = new Timer(new TimerCallback(JobCallBack), null, 1000, 1000);
        }

        private void JobCallBack(object state)
        {
            _timer.Change(Timeout.Infinite, Timeout.Infinite);
            Console.WriteLine("Starting...");
            Thread.Sleep(10000);
            Console.WriteLine("Complete");
            _timer.Change(1000, 1000);
        }

        public void Dispose()
        {
            if (_timer != null)
            {
                _timer.Dispose();
                _timer = null;
            }
            Console.WriteLine("Disposed");
        }
    }
}
以上代码输出结果为:
Starting...
Complete
Starting...
Complete
Starting...
Disposed
Complete

未处理的异常:  System.NullReferenceException: 未将对象引用设置到对象的实例。
   在 ConsoleApplication1.TestClass.JobCallBack(Object state) 位置 C:\ConsoleA
pplication1\Program.cs:行号 35
   在 System.Threading._TimerCallback.TimerCallback_Context(Object state)
   在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, C
ontextCallback callback, Object state)
   在 System.Threading._TimerCallback.PerformTimerCallback(Object state)

------------------------------------------
开始程序是正常运行的,但调用Dispose后程序发生异常,经过我分析,发现问题如下:
主程序方法 Main 中调用了Dispose,因此,TestClass 中的 _timer 为null了,而此时TestClass中的JobCallBack方法尚未结束,因此调用_timer.Change自然会出错了。

请问这该如何解决呢?谢谢大家了!

8 个解决方案

#1


你在使用timer的时候,进行释放的时候,可以通过TimerExampleState来做处理。参看msdn中的例子。

如果不想再运行Timer了,最好在callback中,不要再用change来进行修改。

#2


谢谢,我先去看看例子。
另外,关于你说的“如果不想再运行Timer了,最好在callback中,不要再用change来进行修改。”
就拿我的那个callback函数来说吧,代码如下:
private void JobCallBack(object state)
{
_timer.Change(Timeout.Infinite, Timeout.Infinite);
Console.WriteLine("Starting...");
Thread.Sleep(10000);
Console.WriteLine("Complete");
_timer.Change(1000, 1000);
}
我的这个callback函数中,用change来修改只是想临时禁止Timer,目的是防止这次调用还没完成,下次调用又来了,但是在我的callback完成后我还是需要继续Timer的。
这个我看CS和Forum2,还有其它一些例子也是这么用的,难道这样用不妥吗?请指点,如果不好的话,那这个该如何弄呢?谢谢!

#3


Knight94(愚翁) 你好,我刚刚看了下你说的TimerExampleState例子,那个例子好像是这样的:
他是在Timer的callback方法中自己判断并结束Timer,这个与我的不一样的。我是要外面控制Timer的结束。正因为我是在外面控制(调用Dispose方法),所以才会出现设置Timer为NULL的时候,而callback还在运行的情况,这就出现了异常。

#4


没错,
首先你需要用类的成员保存TimerExampleState,然后在CallBack函数中判断state,或者可以用一个成员来标示要进行dispose操作,
那么在类的Dispose中,先告诉当前状态,然后判断TimerExampleState对象是否已经关闭,然后带调用Timer的dispose。

#5


sample code as follows:
public class TestClass : IDisposable
{
private Timer _timer;
private bool blnDisposed = false;

public void Start()
{
_timer = new Timer(new TimerCallback(JobCallBack), null, 1000, 1000);
}

private void JobCallBack(object state)
{
_timer.Change(Timeout.Infinite, Timeout.Infinite);
Debug.WriteLine("Starting...");
Thread.Sleep(10000);
Debug.WriteLine("Complete");
if( !blnDisposed )
_timer.Change(1000, 1000);
else
_timer = null;
}

public void Dispose()
{
if (_timer != null)
{
blnDisposed = true;
while(_timer != null)
Thread.Sleep(5);
}
Debug.WriteLine("Disposed");
}
}

#6


谢谢,太谢谢了!

#7


另外,我还想问问,我看到一种这样的用法:
public void Dispose()
{
    if (_timer != null)
    {
        lock(this)
        {
            blnDisposed = true;
            while(_timer != null)
                Thread.Sleep(5);
        }
}
我觉得这里加lock没有任何必要吧?你觉得呢?谢谢!

#8


to 我觉得这里加lock没有任何必要吧?你觉得呢?谢谢!

其实类的dispose这个部分,不会在多线程中去掉用,因此我的想法和你一样,是没有必要的。

#1


你在使用timer的时候,进行释放的时候,可以通过TimerExampleState来做处理。参看msdn中的例子。

如果不想再运行Timer了,最好在callback中,不要再用change来进行修改。

#2


谢谢,我先去看看例子。
另外,关于你说的“如果不想再运行Timer了,最好在callback中,不要再用change来进行修改。”
就拿我的那个callback函数来说吧,代码如下:
private void JobCallBack(object state)
{
_timer.Change(Timeout.Infinite, Timeout.Infinite);
Console.WriteLine("Starting...");
Thread.Sleep(10000);
Console.WriteLine("Complete");
_timer.Change(1000, 1000);
}
我的这个callback函数中,用change来修改只是想临时禁止Timer,目的是防止这次调用还没完成,下次调用又来了,但是在我的callback完成后我还是需要继续Timer的。
这个我看CS和Forum2,还有其它一些例子也是这么用的,难道这样用不妥吗?请指点,如果不好的话,那这个该如何弄呢?谢谢!

#3


Knight94(愚翁) 你好,我刚刚看了下你说的TimerExampleState例子,那个例子好像是这样的:
他是在Timer的callback方法中自己判断并结束Timer,这个与我的不一样的。我是要外面控制Timer的结束。正因为我是在外面控制(调用Dispose方法),所以才会出现设置Timer为NULL的时候,而callback还在运行的情况,这就出现了异常。

#4


没错,
首先你需要用类的成员保存TimerExampleState,然后在CallBack函数中判断state,或者可以用一个成员来标示要进行dispose操作,
那么在类的Dispose中,先告诉当前状态,然后判断TimerExampleState对象是否已经关闭,然后带调用Timer的dispose。

#5


sample code as follows:
public class TestClass : IDisposable
{
private Timer _timer;
private bool blnDisposed = false;

public void Start()
{
_timer = new Timer(new TimerCallback(JobCallBack), null, 1000, 1000);
}

private void JobCallBack(object state)
{
_timer.Change(Timeout.Infinite, Timeout.Infinite);
Debug.WriteLine("Starting...");
Thread.Sleep(10000);
Debug.WriteLine("Complete");
if( !blnDisposed )
_timer.Change(1000, 1000);
else
_timer = null;
}

public void Dispose()
{
if (_timer != null)
{
blnDisposed = true;
while(_timer != null)
Thread.Sleep(5);
}
Debug.WriteLine("Disposed");
}
}

#6


谢谢,太谢谢了!

#7


另外,我还想问问,我看到一种这样的用法:
public void Dispose()
{
    if (_timer != null)
    {
        lock(this)
        {
            blnDisposed = true;
            while(_timer != null)
                Thread.Sleep(5);
        }
}
我觉得这里加lock没有任何必要吧?你觉得呢?谢谢!

#8


to 我觉得这里加lock没有任何必要吧?你觉得呢?谢谢!

其实类的dispose这个部分,不会在多线程中去掉用,因此我的想法和你一样,是没有必要的。