Skip to main content
deleted 234 characters in body
Source Link
user1430
user1430

Your problem is inherently serial -- you must complete an update of the simulation before you can render it. Offloading the simulation to a different thread simply means the main UI thread does nothing while the simulation thread ticks (which means it is blocked). I strongly recommend

The commonly held "best practice" for concurrency is not tryingnot to take a concurrent approach forput your specific problem, it's not the ideal solutionrendering on one thread and it will gain you nothing but complexity for basically no payoff.

That saidyour simulation on another, as you can alleviate the issue by isolating the data consumed by either threadare proposing. Split the render data out of the simulation data entirely (this is good practiceI strongly recommend against that approach, in general)fact. The main thread can constant render from the render datatwo operations are naturally serially related, and the simulation threadwhile they can constantly update its databe brute forced, it's not optimal and it does not scale.

There will be someA better approach is to make parts of this data that are inherently sharedthe update or rendering concurrent, but leave updating and in any case you'll need to update the render thread's data with the results of the simulation datarendering themselves always serial. You can do this by locking and copyingSo for example, if you must, but that's crude (and a lock is essentially a block anyhow). You could also usehave a concurrentnatural boundary in your simulation (locklessfor example, if houses never affect each other in your simulation) queue to enqueue messages containing state updates toyou can shove all the rendererhouses into buckets of N houses, which it can apply prior to starting a new render pass. Note that this tends to imply that you are renderingand spin up a frame (or several) behind the actual statebunch of threads that each process one bucket, and let those threads join before the simulationupdate step is complete. This scales much better and can be tedious to debugis a much better fit for concurrent design.

You're overthinkingover-thinking the rest of the issue:

Dependency injection is a red herring here: all dependency injection really means is that you pass ("inject") the dependencies of an interface to instances of that interface, typically during construction.

That means if you have a class that models a House, which needs to know things about the City that it is in, then the House constructor might look like:

public House( City containingCity ) {
  m_city = containingCity; // Store in a member variable for later access
  ...
}

Nothing special.

Using a singleton is unnecessary (you often see it done in some of the insanely complex, over-engineered "DI frameworks" like Caliburn that are designed for "enterprise" GUI applications -- this does not make it a good solution). In fact, introducing singletons is often the antithesis of good dependency management. They also can cause serious problems with multithreaded code because they cannot usually be made thread-safe without locks -- the more locks you must acquire, the worse your problem was suited for handling in a parallel nature.

Your problem is inherently serial -- you must complete an update of the simulation before you can render it. Offloading the simulation to a different thread simply means the main UI thread does nothing while the simulation thread ticks (which means it is blocked). I strongly recommend not trying to take a concurrent approach for your specific problem, it's not the ideal solution and it will gain you nothing but complexity for basically no payoff.

That said, you can alleviate the issue by isolating the data consumed by either thread. Split the render data out of the simulation data entirely (this is good practice in general). The main thread can constant render from the render data and the simulation thread can constantly update its data.

There will be some parts of this data that are inherently shared, and in any case you'll need to update the render thread's data with the results of the simulation data. You can do this by locking and copying if you must, but that's crude (and a lock is essentially a block anyhow). You could also use a concurrent (lockless) queue to enqueue messages containing state updates to the renderer, which it can apply prior to starting a new render pass. Note that this tends to imply that you are rendering a frame (or several) behind the actual state of the simulation and can be tedious to debug.

You're overthinking the rest of the issue:

Dependency injection is a red herring here: all dependency injection really means is that you pass ("inject") the dependencies of an interface to instances of that interface, typically during construction.

That means if you have a class that models a House, which needs to know things about the City that it is in, then the House constructor might look like:

public House( City containingCity ) {
  m_city = containingCity; // Store in a member variable for later access
  ...
}

Nothing special.

Using a singleton is unnecessary (you often see it done in some of the insanely complex, over-engineered "DI frameworks" like Caliburn that are designed for "enterprise" GUI applications -- this does not make it a good solution). In fact, introducing singletons is often the antithesis of good dependency management. They also can cause serious problems with multithreaded code because they cannot usually be made thread-safe without locks -- the more locks you must acquire, the worse your problem was suited for handling in a parallel nature.

Your problem is inherently serial -- you must complete an update of the simulation before you can render it. Offloading the simulation to a different thread simply means the main UI thread does nothing while the simulation thread ticks (which means it is blocked).

The commonly held "best practice" for concurrency is not to put your rendering on one thread and your simulation on another, as you are proposing. I strongly recommend against that approach, in fact. The two operations are naturally serially related, and while they can be brute forced, it's not optimal and it does not scale.

A better approach is to make parts of the update or rendering concurrent, but leave updating and rendering themselves always serial. So for example, if you have a natural boundary in your simulation (for example, if houses never affect each other in your simulation) you can shove all the houses into buckets of N houses, and spin up a bunch of threads that each process one bucket, and let those threads join before the update step is complete. This scales much better and is a much better fit for concurrent design.

You're over-thinking the rest of the issue:

Dependency injection is a red herring here: all dependency injection really means is that you pass ("inject") the dependencies of an interface to instances of that interface, typically during construction.

That means if you have a class that models a House, which needs to know things about the City that it is in, then the House constructor might look like:

public House( City containingCity ) {
  m_city = containingCity; // Store in a member variable for later access
  ...
}

Nothing special.

Using a singleton is unnecessary (you often see it done in some of the insanely complex, over-engineered "DI frameworks" like Caliburn that are designed for "enterprise" GUI applications -- this does not make it a good solution). In fact, introducing singletons is often the antithesis of good dependency management. They also can cause serious problems with multithreaded code because they cannot usually be made thread-safe without locks -- the more locks you must acquire, the worse your problem was suited for handling in a parallel nature.

Source Link
user1430
user1430

Your problem is inherently serial -- you must complete an update of the simulation before you can render it. Offloading the simulation to a different thread simply means the main UI thread does nothing while the simulation thread ticks (which means it is blocked). I strongly recommend not trying to take a concurrent approach for your specific problem, it's not the ideal solution and it will gain you nothing but complexity for basically no payoff.

That said, you can alleviate the issue by isolating the data consumed by either thread. Split the render data out of the simulation data entirely (this is good practice in general). The main thread can constant render from the render data and the simulation thread can constantly update its data.

There will be some parts of this data that are inherently shared, and in any case you'll need to update the render thread's data with the results of the simulation data. You can do this by locking and copying if you must, but that's crude (and a lock is essentially a block anyhow). You could also use a concurrent (lockless) queue to enqueue messages containing state updates to the renderer, which it can apply prior to starting a new render pass. Note that this tends to imply that you are rendering a frame (or several) behind the actual state of the simulation and can be tedious to debug.

You're overthinking the rest of the issue:

Dependency injection is a red herring here: all dependency injection really means is that you pass ("inject") the dependencies of an interface to instances of that interface, typically during construction.

That means if you have a class that models a House, which needs to know things about the City that it is in, then the House constructor might look like:

public House( City containingCity ) {
  m_city = containingCity; // Store in a member variable for later access
  ...
}

Nothing special.

Using a singleton is unnecessary (you often see it done in some of the insanely complex, over-engineered "DI frameworks" like Caliburn that are designed for "enterprise" GUI applications -- this does not make it a good solution). In fact, introducing singletons is often the antithesis of good dependency management. They also can cause serious problems with multithreaded code because they cannot usually be made thread-safe without locks -- the more locks you must acquire, the worse your problem was suited for handling in a parallel nature.