CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 15 of 17

Threaded View

  1. #11
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Re: Trying to multithread program. C++/CLI

    Ok, my VC++ 2010 refused to open your VS 2012 project, so I first needed to build a new version 2010 project out of your source files, which took some additional time. Now I can't use Forms Designer on your forms, but that doesn't really hurt by now. So here we go...

    First I'll give you some mostly syntactical advice to make your project compile with minimal modifications so mou may further purse your current approach if you want, though I'd actually like to suggest you a different approach, more on that below.

    So now I know that your types MotionThread and AllFormVariables actually are reference types, not value types, just your instances of these are implicitly dereferenced variables, which is why you use the value type-like dot operator syntax for member access. Although implicitly dereferenced variables are convenient for developers coming from native C++ because they behave more like native C++ class member or local variales, I personally find them confusing (as already mentioned in earlier threads). This is the fixed version of MyForm::StartThread():

    Code:
         private: Void startThread(){
                    MoveProj.Velocity = Variables.Velocity;
                    MoveProj.ProjectilePos = Projectile1.ProjectilePos;
                    Thread^ MotionThread1 = gcnew 
                      Thread(gcnew ParameterizedThreadStart(%MoveProj ,&MotionThread::MoveProjectile));
                    Thread^ MainThread = gcnew Thread(gcnew ThreadStart(%Variables, &Allformvariables::CalcCurrentVelocity));
                  }
    The % prefixing the first parameters to the delegate constructors, which are specific to the use of implicitly dereferenced variables and were missing, were the reason for the C2440 error message.

    I also changed the signature of Allformvariables::CalcCurrentVelocity() by removing the parameter which was unused anyway, so it now matches the signature expected by the ThreadStart constructor.

    Your global variable timetaken caused a linker error LNK2005 because it was defined non-extern in a header file included by more than one compilation unit. This is a really common mistake with global variables in native C++ which has been discussed quite some times in the Non-VC++ forum section, including how to fix it. However, global variables should be avoided whenever possible in general, and even more so in .NET, so I won't go into more detail about that now. As a quick and hackish fix I turned that variable into a static local variable of Allformvariables::CalcCurrentVelocity() which comes closest to a global variable and was easily possible since it isn't accessed from anywhere else anyway:

    Code:
    public: void CalcCurrentVelocity()
            {
              static float timetaken;
              Velocity.X = initialVelocity.X + (AirResistance/Area)*timetaken;
              Velocity.Y = initialVelocity.Y + Gravity*timetaken;
            }
    However, as is obvious now, the variable, zero-initialized as a static, never gets changed, so the velocity remains constant. This is why that "solution" is quite hackish.

    There also were two (IIRC) C4715 warnings which I didn't pursue further, but you should definitely do that, since they usually indicate severe bugs waiting to sneak up from behind.

    And while we're talking about warnings: There's absolutely no point in using floats rather than doubles in .NET except when you're storing a whole lot of them (for instance, I have an app where I store hundreds of thousands of objects containing some of them), so you can expect a considerable reduction of your memory footprint from using floats, or perhaps when you're working with/supporting interfaces that deal with them. In calculations they actually are quite counter-productive: In any case they're converted to double before performing the calculation, even if the expression being calculated exclusively contains floats, and then converted back when you store the result in a float variable. And changing that would eliminate the majority of the many warnings your code generates, unless you have turned them off (which, in fact, I was really tempted to do, but that's generally not recommended).

    And now for the alternative approach I'd like to suggest, as mentioned at the start of this post: Starting two worker threads for so tiny tasks as you're doing now definitely is overkill, even more so as your thread functions currently are "one-shots", performing their tiny tasks and then terminating (the worker thread just started!). Doing things like that inline in fact is much more efficient, and you didn't even take any care of synchronization yet...

    You wrote you experimented with a timer (am I right to assume you meant a System::Windows::Forms::Timer?) but weren't satisfied with the result. There are two important things worth to know about this kind of timer, which are due to the way it is implemented by the framework: (1) No matter how low you set its Interval property, it will never fire faster than every 15.625 ms (64 Hz). (2) The timer is in no way reliable in terms of real-time processing. If the OS or your app is busy, any number of timer ticks may be missed.

    Still I think you can put up a decent timer-based design for your app, therby avoiding the increased complexity incurred by multithreading. I'm thinking of a design based on the idea of a game loop, yet not with an actual loop but with event-driven iterations done in a timer tick handler. (IIRC this basically is the common game loop implementation approach used with the XNA framework.) The trick is to not base your timing on counting timer ticks, but instead use a reliable independent timestamp source. The System::Diagnostics::Stopwatch class is a great candidate for this. Per my experience it can be expected to have a nice resolution of less than one microsecond and is reliably isochronuous. It can assume the place of your timetaken variable.

    Finally, a word about your random number generation: Re-seeding the random number generator based on time() each time you need a random number is even much worse than not seeding it at all in most cases. This has been discussed quite some times in the Non-VC++ section as well. And I'm not going into detail about that as well, since unnecessarily using native library functions in a .NET program should be avoided whenever possible anyway. The .NET framework provides the System::Random class you can use instead.
    Last edited by Eri523; April 12th, 2013 at 09:26 PM.
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured