如何正确取消订阅活动

时间:2016-10-04 12:33:08

标签: c#

如何正确取消订阅活动,并确保现在不调用被叫方法?

我的问题在于这种代码:

public class MyClassWithEvent
{
    public event EventHandler MyEvent;
    public int Field;
}

public class MyMainClass
{
    private MyClassWithEvent myClass;

    public void Start()
    {
        myClass.MyEvent += new EventHandler(doSomething);
    }

    public void Stop()
    {
        myClass.MyEvent -= new EventHandler(doSomething);
        myClass = null;
    }

    private void doSomething()
    {
        myClass.Field = 42;
    }
}

如果在执行myClass = null时调用doSomething,则指令myClass.Field = 42会引发错误,因为myClass为空。

在设置doSomething之前,如何确定myClass = null没有执行?

修改

其他例子:

public void Stop()
{
    myClass.MyEvent -= new EventHandler(doSomething);

    // Can I add a function here to be sure that doSomething is not running ?

    myClass.Field = 101;
}

在这种情况下,我不确定myClass.Field是42还是101。

EDIT2:

显然我的问题并不像我想象的那么简单。我将解释我的确切案例。

我的代码是:

public class MyMainClass
{
    object camera;//can be type uEye.Camera or DirectShowCamera
    bool isRunning = false;

    public void Start()
    {
        if (camera is uEye.Camera)
        {
            camera.EventFrame += new EventHandler(NewFrameArrived);
        }
        else if (camera is DirectShowCamera)
        {
            //other init
        }
        isRunning = true;
    }

    public void Stop()
    {
        if (camera is uEye.Camera)
        {
            camera.EventFrame -= new EventHandler(NewFrameArrived);
            camera.exit;
        }
        else if (camera is DirectShowCamera)
        {
            //other stop
        }
        isRunning = false;
    }

    public void ChangeCamera(object new camera)
    {
        if (isRunning)
            Stop()
        camera = new camera();
    }

    void NewFrameArrived(object sender, EventArgs e)
    {
        uEye.Camera Camera = sender as uEye.Camera;
        Int32 s32MemID;
        Camera.Memory.GetActive(out s32MemID);

        lock (_frameCameralocker)
        {
            if (_frameCamera != null)
                _frameCamera.Dispose();
            _frameCamera = null;
            Camera.Memory.ToBitmap(s32MemID, out _frameCamera);
        }

        Dispatcher.Invoke(new Action(() =>
        {
            lock (_frameCameralocker)
            {
                var bitmapData = _frameCamera.LockBits(
                    new System.Drawing.Rectangle(0, 0, _frameCamera.Width, _frameCamera.Height),
                    System.Drawing.Imaging.ImageLockMode.ReadOnly, _frameCamera.PixelFormat);

                if (_frameCamera.PixelFormat == System.Drawing.Imaging.PixelFormat.Format8bppIndexed)
                {
                    DeviceSource = System.Windows.Media.Imaging.BitmapSource.Create(
                                                        bitmapData.Width, bitmapData.Height, 96, 96, System.Windows.Media.PixelFormats.Gray8, null,
                                                        bitmapData.Scan0, bitmapData.Stride * bitmapData.Height, bitmapData.Stride);
                }

                _frameCamera.UnlockBits(bitmapData);

                if (OnNewBitmapReady != null)
                    OnNewBitmapReady(this, null);
            }
        }));
    }
}

当我将相机从uEye更改为directshow时,我在DeviceSource = System.Windows.Media.Imaging.BitmapSource.Create(方法NewFrameArrived)中有一个AccessViolationException,因为我尝试从已退出的相机创建BitmapSource

4 个答案:

答案 0 :(得分:2)

从您更新的问题中,您唯一需要做的就是锁定与Stop()

相同的锁中的Dispatcher.Invoke操作
public void Stop()
{
    lock(_frameCameralocker)
    {
        if (camera is uEye.Camera)
        {
            camera.EventFrame -= new EventHandler(NewFrameArrived);
            camera.exit;
        }
        else if (camera is DirectShowCamera)
        {
            //other stop
        }
        isRunning = false;
    }
}

这将确保在您创建新相机之前所有NewFrameArrived来电已完成或尚未开始。然后在调度程序内部检查您是否正在运行,以防万一在启动并完成Stop()调用之前帧已排队。

    Dispatcher.Invoke(new Action(() =>
    {
        lock (_frameCameralocker)
        {
            if(!isRunning)
                return;

            var bitmapData = _frameCamera.LockBits(
                new System.Drawing.Rectangle(0, 0, _frameCamera.Width, _frameCamera.Height),
                System.Drawing.Imaging.ImageLockMode.ReadOnly, _frameCamera.PixelFormat);

            if (_frameCamera.PixelFormat == System.Drawing.Imaging.PixelFormat.Format8bppIndexed)
            {
                DeviceSource = System.Windows.Media.Imaging.BitmapSource.Create(
                                                    bitmapData.Width, bitmapData.Height, 96, 96, System.Windows.Media.PixelFormats.Gray8, null,
                                                    bitmapData.Scan0, bitmapData.Stride * bitmapData.Height, bitmapData.Stride);
            }

            _frameCamera.UnlockBits(bitmapData);

            if (OnNewBitmapReady != null)
                OnNewBitmapReady(this, null);
        }
    }));

答案 1 :(得分:1)

Monitor好用吗?

这个想法是你使用一个锁来确保你没有在(几乎)同时两次使用相同的资源:

public class MyClassWithEvent
{
    public event EventHandler MyEvent;
    public int Field;
}

public class MyMainClass
{
    private MyClassWithEvent myClass;
    private object mylock;

    public void Start()
    {
        myClass.MyEvent += new EventHandler(doSomething);
    }

    public void Stop()
    {
        myClass.MyEvent -= new EventHandler(doSomething);
        Monitor.Enter(mylock); //If somebody else already took the lock, we will wait here
        myClass = null;
        Monitor.Exit(mylock); //We release the lock, so others can access it
    }

    private void doSomething()
    {
        Monitor.Enter(mylock);
        if myClass != null
        {
            myClass.Field = 42;
        }
        Monitor.Exit(mylock);
    }
}

修改

根据评论,Lock会更好用(actually a short-hand for Monitor):

public class MyClassWithEvent
{
    public event EventHandler MyEvent;
    public int Field;
}

public class MyMainClass
{
    private MyClassWithEvent myClass;
    private object mylock;

    public void Start()
    {
        myClass.MyEvent += new EventHandler(doSomething);
    }

    public void Stop()
    {
        myClass.MyEvent -= new EventHandler(doSomething);
        lock (mylock) //If somebody else already took the lock, we will wait here
        {
            myClass = null;
        } //We release the lock, so others can access it
    }

    private void doSomething()
    {
        lock(mylock)
        {
            if myClass != null
            {
                myClass.Field = 42;
            }
        }
    }
}

答案 2 :(得分:0)

相反

softwareupdate -l

待办事项

myClass.Field = 42;

答案 3 :(得分:0)

使用C#6 ......

 myClass?.Field = 42;