0

I have MainForm class and some Engine class for camera control. In Engine class, I have some Camera object, which is part of SDK from camera producer. This Camera has available OnNewFrame event, so I'll initialize:

camera.OnNewFrame += frameAcquired;

frameAcquired is also member of Engine class.

 private void frameAcquired(object sender, EventArgs e)
 {
    /* this event should periodically raise after ~17ms,
       but sometimes it hangs for a short time 
       (when I overloads main thread) */
 }

The Engine object is a member of MainForm class. Here I am displaying images from camera and doing some other graphics stuff. The problem is that MainForm thread sometimes hangs for a very short time. It's not so critical for displaying, but it is for camera.OnNewFrame event (I'm working with 60 fps), as this is also delayed because of main thread delay.

Is it possible to ensure some way, that Engine object (or Camera object in Engine) will raise event's from it's own thread, not from main thread? Other words, ensure that this event raises in rate which SDK producer has set, not dependent on my main thread.

Majak
  • 1,543
  • 1
  • 14
  • 28
  • 2
    Pretty unclear why this is a problem, you'll have to copy the image if this makes the camera firmware blow up for some reason. Ultimately there is basic firehose problem, when the camera spits out images faster than the UI thread can display them, and you are certainly pushing the limits with 60 fps if you don't use any kind of video acceleration, then you are going to lose an update occasionally. – Hans Passant Feb 23 '15 at 12:31
  • The displaying isn't problem. I am using some queue of frames, and in case that the old frame wasn't displayed yet, I'll throw away displaying of the new one. But I need to save them all (doing this on another thread without problems). But sometimes when main thread hangs (for example resizing of form -> some calculations in my code, and other cases), event raising hangs too. – Majak Feb 23 '15 at 12:36
  • "Event raising" only hangs if you make the mistake of using Invoke() instead of BeginInvoke(). – Hans Passant Feb 23 '15 at 12:40
  • You can use `BeginInvoke` instead of `Invoke` (`InvokeAsync` in wpf), that would not block your event processing. Or you can execute event handler in separate thread (so if one is blocked others are still processing), this has to include synchronization. But of course correct approach is to implement some sort of consumer-producer queue, where screen takes last frame. – Sinatr Feb 23 '15 at 12:40
  • I'm not using `Invoke` nor `BeginInvoke` here. I create `Engine` object, and in `Engine` class I'm initializing event. Can you suggest me how should I use `BeginInvoke` ? I don't know how the event is raised inside of SDK unfortunately. – Majak Feb 23 '15 at 12:45
  • Can you show what SDK you are using, there are "generic" solutions to quickly push the work off to a background thread after the event is raised on the UI thread, but a solution specific to your SDK to get it to not raise the event on the UI thread at all will give you better performance results. – Scott Chamberlain Feb 23 '15 at 14:18
  • Sorry I don't have any documentation for this as it is being creating to order, only dll which has copyright. – Majak Feb 23 '15 at 14:56

1 Answers1

1

I have ran into a similar problem not too long ago. I have dealt with it in C++/CLI so the same approach should also work in C# too.

I believe you have the Engine class initialized in your MainForm. If you want to raise events from another thread then this object has to be initialized in another thread.

I believe you should try creating a new Thread in your MainForm constructor:

MyForm()
{
    //rest of your constructor
    cameraThread = new Thread(new ParameterizedThreadStart(CameraRun));
    cameraThread.Name = "Camera Thread";
    cameraThread.Start(this);
    while (!cameraThread.IsAlive)
        Thread::Sleep(1);
}

This way you can keep a field for cameraThread in your MyForm class. Still, you need to write a function for the new thread to run. We know it will initialize your Engine class but that is not all. Just to make sure the thread doesn't finish the function you gave it to run, add some check at the bottom of thread function like this:

void CameraRun(Object myForm)
{
    //you can use (myForm as MyForm)
    //and make calls from here
    /*
    Engine initialization etc
    */
    while((myForm as MyForm).isShown)
        Sleep(100);
}

cameraThread should join back to your main code in MainForm destructor.

~MyForm()
{
    //rest of your destructor
    this.isShown=false;
    cameraThread.Join();
}

You can put the line this.isShown=false to your OnFormClosed() event if you wish.

If you have come this far, great. However you are not done yet, unfortunately. As you are now working on multiple threads you have to make sure you access objects in a thread safe manner. Long story short, check this answer.

Edit: some corrections

Community
  • 1
  • 1
  • I know you came from C++ but C#'s "deconstructors" are not like C++. You should not be doing stuff like `this.isShown=false;` in the `~MyForm()` method. Also `while (!cameraThread->IsAlive) Thread.Sleep(1);` will lock up the UI in C#. – Scott Chamberlain Feb 23 '15 at 14:19
  • I don't understand why not? Care to explain? Edit: It will, until the thread is alive. – Bahadir Acar Feb 23 '15 at 14:21
  • isShown is just a boolean field to keep track if the form is still on display or not. – Bahadir Acar Feb 23 '15 at 14:22
  • Ok, you may be able to get away with setting a Boolean, but `~` does not do a "destructor" in C#, it creates a [Finalizer](https://msdn.microsoft.com/en-us/library/vstudio/b1yfkh5e(v=vs.100).aspx) which has very different behavior than a destructor. The most major one is it is not ordered or deterministic, you could have members you own finalized before you get a chance to finalize, this can cause you to attempt to access a object that no longer exists. – Scott Chamberlain Feb 23 '15 at 14:26
  • in C# `class classname { ~classname() {} }` is the same as `class classname { !classname() {} };` in C++. See [Destructors and Finalizers in Visual C++](https://msdn.microsoft.com/en-us/library/vstudio/ms177197(v=vs.100).aspx) on the MSDN. – Scott Chamberlain Feb 23 '15 at 14:30
  • Interesting point. Thanks for the tip. I haven't received any error due to this so far though... If it is not ordered then I guess we could call `cameraThread.Join()` during FormClosed event? – Bahadir Acar Feb 23 '15 at 14:32
  • That or take the `override void Dispose(bool disposing)` out of the designer file and put it in your main code and put it inside the `if(disposing) { }` block. – Scott Chamberlain Feb 23 '15 at 14:34