• Advertisement
Sign in to follow this  

Unity Threads + Concurrency & Weird Issue

This topic is 1849 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello all, I been having a weird issue with my code that I can't seem to get working so I thought I'd try asking the community.

Basically I have created a class called TimerManaged which extends a thread, this class has a loop that runs in its run() method that checks any TimedEvent added to it. the TimedEvent class is abstract and is overwritten by whatever class needs it, it does however have a float timeReference variable that references its start time with respect to the global time of TimerManaged.

The TimerManaged checks all these TimedEvents by calling their monitor method that checks to see if anything was suppose to happen at time T, if it was then execute that event.

Now the issue I'm having is that the events only get executed in the first run through, since alot of these events repeat at a later time the TimedEvent class also has a reset method that essentially resets its local time back to zero with respect to the global time. But the reset doesnt seem to work properly UNLESS i add a System.out.println("" + TimedEvent.LocalTime) call in either the TimedEvents.monitor() method, or inside the loop that is calling the TimedEvents monitor class. Things work perfectly when I have that system.out there, but it doesnt work other wise. I tried modifieing the LocalTime variable for the TimedEvents volatile, nothing was fixed. I also modified the add, remove, monitor for the TimedEvents and TimerManaged synchronized, but again this didn't fix anything.

I'm really pulling my hairs out with this issue so any help will be greatly appreciated. Thank you.

Share this post


Link to post
Share on other sites
Advertisement
Could you provide some code?

Also it almost sounds as if you are creating a thread for each event? And using another thread to activate them? Threads aren't some thing you should create a lot of. They will mostly just give you trouble. If you want to add timed events you should either only have one thread doing it, or even better, add a priority queue in your game loop and perform and remove/reinsert the event at the top of the queue if its time has expired.

But any ways post your code, then I'll take a look at it tomorrow :)

Share this post


Link to post
Share on other sites
Sorry bout the delay heres the 2 main classes

[source lang="java"]public class TimerManaged extends Thread
{
// TimerManaged can be either put into a do while loop, or run as a thread
Vector<TimedEvent> tes = new Vector<TimedEvent>();
float gTimeSRef; // in seconds
long gTimeTRef; // in ticks

float gTimeS;
long gTimeT;
TimedEventListener tel = null;

public TimerManaged()
{

}

public void setTimedEventListener(TimedEventListener _tel)
{
tel = _tel;
}

@Override public void run()
{
try
{
startGlobalTime();
while(true)
{
monitor();
}
}
catch(Exception e)
{

}
}

public void startGlobalTime()
{
gTimeTRef = System.nanoTime();
gTimeSRef = gTimeTRef/1000000000f;
}

public float ticksToSeconds(long ticks)
{
return ticks/1000000000f;
}

public long getGlobalTimeTicks()
{
return System.nanoTime() - gTimeTRef;
}

public float getGlobalTimeS()
{
return getGlobalTimeTicks()/1000000000f;
}

public synchronized void addTimedEvent(TimedEvent _te)
{
tes.add(_te);
}

public synchronized void removedTimedEvent(TimedEvent _te)
{
tes.remove(_te);
}

public synchronized void clearTimedEvents()
{
tes.clear();
}

public synchronized void monitor()
{
gTimeT = System.nanoTime() - gTimeTRef;
gTimeS = gTimeT/1000000000f;


for(int i=0; i < tes.size(); i++)
{
TimedEvent te = tes.get(i);

if(te.monitor(te.getLocalTime(gTimeS)))
{
// something happened
tel.interpretTimedMessage("", te, te.getLocalTime(gTimeS));
}
}
}
}[/source]

and

[source lang="java"]public abstract class TimedEvent
{
TimerManaged mngr;

volatile long lTimeTRef;
volatile float lTimeSRef;

public TimedEvent(TimerManaged _mngr)
{
mngr = _mngr; // all time events need a manager
}

public void setManager(TimerManaged _mngr)
{
mngr = _mngr;
}

public float getLocalTime(float totalTimePassed)
{
// System.out.println("TE[getLocalTime] " + (totalTimePassed - lTimeSRef));
return totalTimePassed - lTimeSRef;
}

public void start()
{
lTimeTRef = mngr.getGlobalTimeTicks();
lTimeSRef = mngr.ticksToSeconds(lTimeTRef);
// System.out.println("TE[Start] " + lTimeSRef);
}

public void reset()
{
lTimeTRef = mngr.getGlobalTimeTicks();
lTimeSRef = mngr.ticksToSeconds(lTimeTRef);
// System.out.println("TE[Reset] " + lTimeSRef);
}

public abstract boolean monitor(float tPassed);
}[/source]

Share this post


Link to post
Share on other sites
[quote]
I tried modifieing the LocalTime variable for the TimedEvents volatile, nothing was fixed. I also modified the add, remove, monitor for the TimedEvents and TimerManaged synchronized, but again this didn't fix anything.
[/quote]
This is not a good way to go about writing multi-threaded code. You need to understand why something is made volatile, and why something is synchronised. If you don't know, then your code is probably broken and not thread safe - even if it appears to work.

Share this post


Link to post
Share on other sites
@Rip-off its actually graphical application, and the issue is anytime I add console output for the TimedEvents localtime, the problem is fixed. However when I take it off it stops working.

Basically the intended use is you create a TimedEvent which is monitors by the TimerManaged class. The TimerManaged runs in its own thread and checks the TimedEvents assigned to it. The issue is that when I use TimedEvent.reset() to force that event to run again, it doesn't. I know synchornized is used for when you have two threads sharing the same information to ensure one completes before another one gets access to that data, and I know volatile is for atomic type operations to ensure that the data is the same across the threads.

Writing this response I start to think that maybe lTimeTRef = mngr.getGlobalTimeTicks(); is the issue since mngr.getGlobalTimeTicks isn't exactly a atomic operations. Ill try to change and see if that fixes anything, but any other suggestions are more then welcomed.

Share this post


Link to post
Share on other sites
[quote name='The_Neverending_Loop' timestamp='1355711729' post='5011473']
[...] and the issue is anytime I add console output for the TimedEvents localtime, the problem is fixed.[/quote]

Most console output will be automatically synchronized. That generally does not fix the problem, but makes it appear more consistent (either consistently breaking or consistently working). The key word here is 'appear', running the program on any other computer or making minor code changes can lead to completely different results.
A lot of things can go wrong with multithreading. It requires a lot of effort and experience to write safe, thread-safe code. I strongly advise reading [b]a lot[/b] about multithreading before writing the first line of code and only trying it out in test cases which are engineered to catch concurrency errors. Only after you have understood why all of your errors appeared and you know exactly how you fixed it ('I changed that and the error disappears' does not cut it - why exactly did the problem happen and why exactly did the change fix it), should you even think about adding multithreading to productive code. Otherwise, multithreading is just a huge pain.
Besides, if you have not really understood the problems above you cannot even decide if something could be multithreaded - a lot of people write 'multithreaded' code and by the time they have it largely bug-free it's so heavily synchronized it would have been better to keep it in one thread.

Share this post


Link to post
Share on other sites
I have two problems with what you're doing. The first is that while the TimerManaged class has used synchronized to protected the shared vector of TimedEvent objects, the TimedEvent objects themselves are not safe. For example, look at the following method in the TimedEvent class:

[source]
public void reset()
{
lTimeTRef = mngr.getGlobalTimeTicks();
lTimeSRef = mngr.ticksToSeconds(lTimeTRef);
// System.out.println("TE[Reset] " + lTimeSRef);
}
[/source]

While you use the volatile keyword to make sure the long values lTimeTRef and lTimeSRef are not cached, that doesn't make the reset() method atomic. The context can switch after setting the lTimeTRef variable but before setting the lTimeSRef variable.

Also, in the TimerManaged, even though the monitor method is synchronized, nothing prevents other threads from modifying the TimedEvent objects.

[source]
if(te.monitor(te.getLocalTime(gTimeS))) // TimedEvent accessed
{
// something happened
tel.interpretTimedMessage("", te, te.getLocalTime(gTimeS)); // TimedEvent could have been changed by now
}
[/source]

Perhaps thinking about what is actually shared between threads would help.

I suspect that the reason adding the System.out.println makes the code work is that this changes the timing
enough to alter the runtime behavior, but that is just a guess.

The second thing is having a super tight while loop continuosly calling a snychronized method is the
wrong way to handle this. One of the import concepts to understand when writing threaded code like this
is that while single threaded code should not block, code like this needs to block whenever there isn't
anything to do.

When the TimerManaged first runs, and there are no events to process, it should block, and wait for
something to be added to the queue. Then, perhaps it sleeps for about as long as the expected message
time, then wakes up, sends the message, and goes back to sleep. As the others have already pointed out,
this is an extremely difficult thing to get right, and the chance for weird, random race conditions that are
almost impossible to trigger on purpose requires that you know what you're doing.

But the best way to learn this stuff is to do it, so drive on. Just know that it will not be easy. If it was me, this solution
seems way too hard, and I'd try and figure out another way to solve the problem without all the threads. Edited by Glass_Knife

Share this post


Link to post
Share on other sites

Thanks glass, I tried fixing the issue but I took your suggesting and figured another way around the problem, I decided to have TimerManage extend TimerTask instead and have its run method be my monitor() call.  This fixed the problem.

 

As for my first attempt with the thread please be advised that alot of what you see in the code came after desperation where it wasn't working where I thought it should so I added synchronized methods where it wouldn't of been practical just because I wanted to try to get my code to work and then work backwards from there.

 

I will try to find a way to implement this with threads in the future but for now extending TimerTask instead is doing a great job, so I recommend anyone that might want to do somethign similair give TimerTask a chance.

Share this post


Link to post
Share on other sites
I will try to find a way to implement this with threads in the future but for now extending TimerTask instead is doing a great job, so I recommend anyone that might want to do somethign similair give TimerTask a chance.

 

When you're ready, I recommend 

 

http://amzn.com/0321349601

Share this post


Link to post
Share on other sites
I will try to find a way to implement this with threads in the future but for now extending TimerTask instead is doing a great job, so I recommend anyone that might want to do somethign similair give TimerTask a chance.

 

When you're ready, I recommend 

 

http://amzn.com/0321349601

Funny u mentioned that, I actually ordered that book this moring (before seeing this post) and it should be here by friday.  I'm a bit worried about it being 6 years old tho, but it seemed to have gotten great reviews.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
  • Advertisement
  • Popular Now

  • Advertisement
  • Similar Content

    • By Atwo Studios
       
      Hey guys,

      Anthony here from Atwo Studios bringing you some new updates for the new year!
      In this video I go over our game ROY, the new games and some general updates to the company!

      If you have not checked out ROY feel free to give it a try! Many people have said they enjoyed the game thus far!
      ROY: https://goo.gl/o6JJ5P
       
    • By Affgoo
      https://play.google.com/store/apps/details?id=com.NE.Alien
      still a lot of work to do, but its pretty stable  please let me know what you think <3
      Atlas Sentry is a game of destroy everything. Using your turret, simply swivel and shoot your way to victory, upgrading your weapons to unleash destruction on the variety of spaceships. The bigger your combo’s the more score you get! Earn silver as you play and then purchase new weapons and abilities to better deal with your enemy. Different enemies use different tactics and weapons, work out your own priorities in their destruction order. 

      Features: 
      **2 different game modes 
      **A level select mode with 20 difficult levels including a final boss, can you defeat it? **Arcade mode of endless destruction, how long will you last? 
      **High scores to compete against others, see who can take the top spot. 
       
    • By Chamferbox
      Chamferbox, a mini game asset store has just opened with some nice game assets, 
      Here you can find a free greek statue asset 

      Also check their dragon, zombie dragon and scorpion monster out:



      They're running the Grand Opening Sale, it's 30% off for all items, but for gamedev member, you can use this coupon code:
      GRANDOPEN
      to get 50% off prices What are you waiting for, go to
      http://chamferbox.com
      and get those models now!

      View full story
    • By Dafu
      FES Retro Game Framework is now available on the Unity Asset Store for your kind consideration!
      FES was born when I set out to start a retro pixel game project. I was looking around for an engine to try next. I tried a number of things, from GameMaker, to Fantasy Consoles, to MonoGame and Godot and then ended up back at Unity. Unity is just unbeatable in it's cross-platform support, and ease of deployment, but it sure as heck gets in the way of proper retro pixel games!
      So I poured over the Unity pipeline and found the lowest levels I could tie into and bring up a new retro game engine inside of Unity, but with a completely different source-code-only, classic game-loop retro blitting and bleeping API. Months of polishing and tweaking later I ended up with FES.
      Some FES features:
      Pixel perfect rendering RGB and Indexed color mode, with palette swapping support Primitive shape rendering, lines, rectangles, ellipses, pixels Multi-layered tilemaps with TMX file support Offscreen rendering Text rendering, with text alignment, overflow settings, and custom pixel font support Clipping Sound and Music APIs Simplified Input handling Wide pixel support (think Atari 2600) Post processing and transition effects, such as scanlines, screen wipes, screen shake, fade, pixelate and more Deploy to all Unity supported platforms I've put in lots of hours into a very detail documentation, you can flip through it here to get an better glimpse at the features and general overview: http://www.pixeltrollgames.com/fes/docs/index.html
      FES is carefully designed and well optimized (see live stress test demo below). Internally it uses batching, it chunks tilemaps, is careful about memory allocations, and tries to be smart about any heavy operations.
      Please have a quick look at the screenshots and live demos below and let me know what you think! I'd love to hear some opinions, feedback and questions!
      I hope I've tickled your retro feels!



      More images at: https://imgur.com/a/LFMAc
      Live demo feature reel: https://simmer.io/@Dafu/fes
      Live blitting stress test: https://simmer.io/@Dafu/fes-drawstress
      Unity Asset Store: https://www.assetstore.unity3d.com/#!/content/102064

      View full story
    • By DevdogUnity

      Ho ho ho
      Tis the season of Christmas surprises, and we have a awesome one for you! 🎅  
      Sponsored by all your favorite Unity Asset Store developers, Nordic Game Jam, Pocket Gamer Connects, and co-hosted by Game Analytics, we (Joris and I – Devdog) are launching the second edition of our yearly Christmas Giveaway Calendar for all Unity game developers!
      You can already now sign up right here.
       
      So what’s this all about?
      For the past weeks, we’ve been collecting sponsored gifts related to Unity (asset vouchers, product keys, conference tickets etc.), and throughout each day of December leading up to Christmas Day on the 25th, we will be sending out these sponsored gifts as early gamedev Christmas presents via e-mail to hundreds of lucky winners.
      The total prize pool is at $35,000, with over 1200 presents donated by the awesome sponsors!
       
      Merry Christmas from Devdog, Game Analytics, and every single one of the sponsors.

      View full story
  • Advertisement