Java-Gaming.org Hi !
Featured games (90)
games approved by the League of Dukes
Games in Showcase (739)
Games in Android Showcase (224)
games submitted by our members
Games in WIP (820)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Making a task queue  (Read 1666 times)
0 Members and 1 Guest are viewing this topic.
Offline Jacob_

Junior Devvie


Projects: 3



« Posted 2010-09-27 21:00:41 »

In my current project, the game entities can be affected by the main game loop, the AWT thread, and later on, network packets or Lua scripts. To make things simple, I want to make a task queue to ensure that all of the actual changes happen in the main thread. This seems like it should work:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
    private static final Vector<Runnable> tasks = new Vector<Runnable>(Settings.MAX_TASKS);

    public static void addTask(Runnable r)
    {
        synchronized(tasks)
        {
            tasks.add(r);
        }
    }
    private static void processTasks()
    {
        synchronized(tasks)
        {
            Iterator<Runnable> itr = tasks.iterator();
            while(itr.hasNext())
            {
                itr.next().run();
                itr.remove();
            }
        }
    }


It usually works OK, but if I get unlucky I get a ConcurrentModificationException in the processTasks() method when changing something using the GUI. Am I doing the synchronization wrong?
Offline Orangy Tang

JGO Kernel


Medals: 57
Projects: 11


Monkey for a head


« Reply #1 - Posted 2010-09-27 21:27:10 »

ConcurrentModificationException doesn't necessarily mean it's been changed in another thread - could Runable.run() also be creating extra tasks and trying to insert them onto the queue while you're iterating over it?

Alternatively you could take a copy of the list and iterate over that, or look into CopyOnWriteArrayList.

[ TriangularPixels.com - Play Growth Spurt, Rescue Squad and Snowman Village ] [ Rebirth - game resource library ]
Offline Eli Delventhal

JGO Kernel


Medals: 42
Projects: 11
Exp: 10 years


Game Engineer


« Reply #2 - Posted 2010-09-27 21:37:22 »

Usually for any risky data collections I never access them directly. Instead, I create a synchronized accessor method for it.

It looks like you're synchronizing everything correctly from the code you've shown, I merely do what I said for sanity's sake.

1  
2  
3  
4  
public synchronized Vector<Runnable> getTasks()
{
    return tasks;
}


Then instead of tasks.add() you call getTasks().add(), etc.

Probably though your issue is what Orangy pointed out - you'll get a ConcurrentModificationException if anything modifies tasks aside from that Iterator, so if run() can add a new task then that'll break everything.

See my work:
OTC Software
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Orangy Tang

JGO Kernel


Medals: 57
Projects: 11


Monkey for a head


« Reply #3 - Posted 2010-09-27 23:11:33 »

1  
2  
3  
4  
public synchronized Vector<Runnable> getTasks()
{
    return tasks;
}


Then instead of tasks.add() you call getTasks().add(), etc.

Maybe I'm being dense, but I can't see how that approach actually enforces anything? It looks like you're still open to concurrent modification.

[ TriangularPixels.com - Play Growth Spurt, Rescue Squad and Snowman Village ] [ Rebirth - game resource library ]
Offline cylab

JGO Kernel


Medals: 173



« Reply #4 - Posted 2010-09-28 07:46:23 »

1  
2  
3  
4  
public synchronized Vector<Runnable> getTasks()
{
    return tasks;
}


Actually this doesn't synchronize anything but the returning of the tasks field. As soon as you call any method on it, it is not synchronized anymore. So this code snippet doesn't provide any improvement in a multithreaded environment.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
    private static final Vector<Runnable> tasks = new Vector<Runnable>(Settings.MAX_TASKS);

    public static void addTask(Runnable r)
    {
        synchronized(tasks)
        {
            tasks.add(r);
        }
    }
    private static void processTasks()
    {
        synchronized(tasks)
        {
            Iterator<Runnable> itr = tasks.iterator();
            while(itr.hasNext())
            {
                itr.next().run();
                itr.remove();
            }
        }
    }


You probably remove or add  task in one of the tasks run() methods. So you change the tasks vector while you are iterating over it. This is a common problem in listener patterns and usually you just copy the vector  to some temporary collection to iterate over it.

Mathias - I Know What [you] Did Last Summer!
Offline nsigma
« Reply #5 - Posted 2010-09-28 09:34:22 »

Why use an Iterator full stop?  It's a queue! You only need thread safety when actually adding or removing each individual task.

ie. something like.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
private static void processTasks() {
  while(true) {
    Runnable task;

    synchronized(tasks) {
       if (tasks.isEmpty()) {
          return;
       } else {
          task = tasks.get(0);
          tasks.remove(0);
       }
    }

    task.run();
 }
}  


You'd be better looking at a different data structure (like a LinkedList) rather than Vector.  Iif you're Java 5+ then just look at ConcurrentLinkedQueue and you can remove all of your synchronization code - just loop through in your processTasks method until poll() returns null.

Best wishes,

Neil

Praxis LIVE - hybrid visual IDE for (live) creative coding
Offline Eli Delventhal

JGO Kernel


Medals: 42
Projects: 11
Exp: 10 years


Game Engineer


« Reply #6 - Posted 2010-09-28 18:25:16 »

Actually this doesn't synchronize anything but the returning of the tasks field. As soon as you call any method on it, it is not synchronized anymore. So this code snippet doesn't provide any improvement in a multithreaded environment.
Shocked Uhduh... what was I thinking.

I've got to read up on synchronization and the right ways to do this, clearly. That means my current WIP has a blaring possibility for a concurrency crash.

See my work:
OTC Software
Offline Jacob_

Junior Devvie


Projects: 3



« Reply #7 - Posted 2010-09-28 20:55:01 »

Quote
ConcurrentModificationException doesn't necessarily mean it's been changed in another thread - could Runable.run() also be creating extra tasks and trying to insert them onto the queue while you're iterating over it?

That was the problem! It wasn't supposed to be doing that, but in the future I might make a copy of the queue and iterate over the copy, rather than the original.
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 
Ecumene (56 views)
2017-09-30 02:57:34

theagentd (79 views)
2017-09-26 18:23:31

cybrmynd (187 views)
2017-08-02 12:28:51

cybrmynd (185 views)
2017-08-02 12:19:43

cybrmynd (192 views)
2017-08-02 12:18:09

Sralse (202 views)
2017-07-25 17:13:48

Archive (758 views)
2017-04-27 17:45:51

buddyBro (892 views)
2017-04-05 03:38:00

CopyableCougar4 (1441 views)
2017-03-24 15:39:42

theagentd (1324 views)
2017-03-24 15:32:08
List of Learning Resources
by elect
2017-03-13 14:05:44

List of Learning Resources
by elect
2017-03-13 14:04:45

SF/X Libraries
by philfrei
2017-03-02 08:45:19

SF/X Libraries
by philfrei
2017-03-02 08:44:05

SF/X Libraries
by SkyAphid
2017-03-02 06:38:56

SF/X Libraries
by SkyAphid
2017-03-02 06:38:32

SF/X Libraries
by SkyAphid
2017-03-02 06:38:05

SF/X Libraries
by SkyAphid
2017-03-02 06:37:51
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!