tags:

views:

91

answers:

3

Lets say I have a class where each object should interact independently. For example, I may have various tables out of a database over many servers offsite. However, internally, I happen to know that some of the tables are linked, and I can save a significant amount of IO by grouping commands that do not require any response together. My goal is to be able to write code using the interface for Table as such:

void bar() {
    // queue time is 500 milliseconds
    Table::queue_time = 500; 
    Table a("foo"),b("baz"),c("cat");

    // Call some function for members of this class
    a.add(2);
    b.add(5);

    // Command is sent to database
    sleep(1);

    // This command will be sent later
    c.add(5);
}

I want this code, in effect, to actually only send two commands to the database. After first calling a.add(), I want a sort of "queue" to begin that after 500ms will issue a command to the database. When b.add() is called, I would like this new instruction to be tossed onto this queue. However, since the sleep happens (and time passes enough for the instruction to send away), I would like c.send() to actually begin a new queue which will send, again, within 500ms.

The trick, however, is that I'd like to do this without threads if possible. Is it even possible (I'm inclined to think not)? If not, what tools in C++ are fit for the job, and how can I communicate with objects that will be edited before being read from while that thread is sleeping?

(In reality, this has nothing to do with databases, so don't worry about the fact that this would be useless for them)

A: 

If you can't use threads, then you must periodically call out to a routine which can test the amount of elapsed time and flush the queue as necessary.

If you want to use threads, you can use a lock of some kind to guarantee mutual exclusion for threads accessing the queue. Acquiring the lock must be done for both reading and writing, and elements in the queue shouldn't be modified after they've been added.

Another way to do it would be to simply buffer elements in the queue, and when the queue gets too large, flush it explicitly.

Barry Kelly
If you write a routine that periodically intervenes, you are actually writing a threading system on your own, which maybe good if you're a library vendor, but won't if you can use an existing one.
xtofl
No, it doesn't "intervene" - you call it yourself. It makes a lot of sense for loop-intensive applications like games, and similarly a common timer functionality of Windows is implemented in the same way, using the message loop.
Barry Kelly
+3  A: 

First of all I suggest you read about the command pattern. Your commands should be objects put one after the other on a stack (a list if you prefer), FIFO (first in first out). But you will need an executor to take every command in the stack and execute it : you need one thread right there. I think you need to be prepared to use threads... you won't be able to avoid them entirely. As for the waiting : a special type of command. A command who waits (put the current thread to sleep). The executor will do every command one after the other and when it will execute the waiting command... well it will wait. And that's it !

Just don't go and start every command in a new thread because you will defeat the purpose of the waiting command.

The undo feature is based on this pattern. You put commands in a stack and execute them then put them in the "done" stack. And if you want to go back, the executor execute each command of the "done" stack "backward". I won't go into implementation details but you can dig deeper if you're interested.

Silence
+2  A: 

"I want a sort of "queue" to begin "

"begin a new queue which will send, again, within 500ms"

The "beginning" of a queue isn't perfectly sensible. Queues are usually passive things.

A "server" would pick commands out of the queue and execute them. So you want a server that normally gets a command and waits 500 ms. Right?

Sometimes you want to override the 500ms wait. Right?

So you have a "scheduling" problem. Not a queuing problem. You have commands what execute at a 500ms scheduling interval, and sometimes you want to vary that scheduling interval.

Note that the schedule is "static". You don't need to sleep in the middle of the schedule. You have a CommandWithAWait class which executes the command and waits 500 ms. Or waits 1 second.

a = CommandWithAWait( Table("foo").add(2), 500 );
b = CommandWithAWait( Table("bar").add(5), 1000 );
c = CommandWithAWait( Talbe("baz").add(5), 500 );

s= Schedule();
s.add( a );
s.add( b );
s.add( c );
s.go();

Something like that appears to be what you're talking about. In this case, the Schedule class can execute the given command, and execute them with appropriate delays. There's no real "thread" business involved.

S.Lott
Great insight to coin the 'scheduling' term! Indeed, the thread business can be a means to execute the schedule.
xtofl
That's quite alike to what I wish to do, but the trick is I never can quite tell when to "go()". I would simply like to assume that it will go after adding the command when the running timer runs out or that it will create a new timer for 500ms.
duckworthd
@duckworthd: The `go` method of `Schedule` is a loop. It executes a command; it sleeps for the required time.
S.Lott