2016-10-04 126 views
1

如何正确取消订阅活动并确保现在未调用所调用的方法?如何正确取消订阅活动

我的问题是这样的代码:

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; 
    } 
} 

如果在doSomething正在执行myClass = null被调用时,指令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

+0

检查在赋值之前它是否为空? – ThePerplexedOne

+0

我的例子可能太简单了,我可以有其他情况下不使用'myClass'。我将编辑我的问题以显示其他情况。 –

+0

@ThePerplexedOne只是检查没有解决问题,因为myClass可以在检查之后和使用之前分配给null。这就是为什么我建议继续参考myClass的本地价值。 – tym32167

回答

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); 
     } 
    })); 
0

相反

myClass.Field = 42; 

val local = myClass; 
if (local != null) 
    local.Field = 42; 
+0

我编辑了我的问题与另一种情况下,您的解决方案将不适用于第二种情况。我正在寻找一个同步机制 –

+0

在这种情况下,看一看'''ReaderWriterLockSlim'''我认为它可能对你有用,因为你正试图控制写操作。 MSDN链接https://msdn.microsoft.com/en-us/library/system.threading.readerwriterlockslim(v=vs.110).aspx – tym32167

0

使用C#6 ...

myClass?.Field = 42; 
+0

这不应该编译。 – tym32167

+0

您使用的是什么版本的C#? –

+1

它不会用任何C#版本编译,因为这是字段/属性赋值 – tym32167

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; 
      } 
     } 
    } 
} 
+0

不需要使用'Monitor'只需使用'lock '。 –

+0

使用锁定或尝试finally块来锁定对象。 – Artiom

+0

我用我自己的锁(请参阅edit2)而不是'Monitor',我现在没有Exception –