Build a 'weight'-based-simulation (Yeah, I just now invented that term). Each variable (regardless of its type) has a 'weight'. For example, players have weights. A good player has extra weight. A player with an injury has less weight or even no wait at all (or maybe negative weight?).
You add all the weight together (of both teams, because it is a soccer match). That weight resembles a winning chance percentage. For example;
The weight of Team A = 56, the weight of Team B = 120
The weight already shows that one team is much better (regardless of how the weight was established .. maybe they have very round balls, who cares) than the other.
Based on the weight, you could calculate a winning chance;
The winning chance of Team A = 32%, The winning chance of Team B = 68%.
Now you could write an algorithm that simulates a match, influenced by the winning percentage. I wrote an algorithm like this once to draw advertisements. In my case, the number of clicks an advertisement had was the weight. The bigger the weight, the more chance the advertisement was picked by my algorithm.
I wrote the algorithm by taking a large number (like, 1000) and then assigned a range of that number to each advertisement, based on the weight percentage. In this case, Team A gets a range of 32% of 1000, which is 0 - 320, Team B gets a range of 68% which is 321 - 1000. Then my algorithm would draw a number (randomly) between 0 and 1000. The advertisement (or your teams) with the largest range (and thus largest winning chance) has the most chance of being picked by the algorithm, although it could turn out differently.
This kind of algorithm is great (although not perfect) for a balanced outcome (if users could create their own teams, buy better players, etc). You could also make any events within the game drawn by this algorithm, simply by adding a weight to the event as well..
You could add weight to an event (for example the injury of a team mate), per team, based on other weight factors within that team (how many matches played in a row, how good is (or how much weighs) their medic staff, etc). If you do the weight thing right, you could get a very balanced (and easily expandable) simulation algorithm that can both be predictable (just like some matches in real life) or totally surprising (again, just like a real life match).
UPDATE:
Tactical Influences
You added tactical influences, plus the question 'how would you do it?', so I will elaborate. What you are currently doing (as I understand it) is you take a percentage (the chance something occurs) and multiply that with a ratio, so that it will occur more/less.
However, because you can have multiple ratio's, you end up with a chance more then 100%.
First of all, for every tactical advantage of a team, there is (probably) a counter advantage on the other team. For example, if Team A has a weight in making goals, Team B has a counter weight in stopping goals. This sum is the universe (100%). Now the weight of both tactical advantages makes up a piece of that universe, or total weight (as I explained above).
Say that Team A is 80% certain of scoring a goal, in a certain minute, and Team B is 20% certain of stopping it (based on the weight system). But, because Team B just acquired a very good keeper, there is a tactical influence on Team B's side. This influence should shift the chance of an event, but not the universe itself! In other words, you shouldn't end up with a total chance of more then 100% (although in some cases, this isn't necessarily a bad thing)
So, you should add weight to Team B, based on the tactical influence and then re-calculate the chances based on the new weights.
Assigning Weight
Now, like you commented, assigning weight isn't easy. Certainly not if you have to 'weigh' players on their qualities. Weighing is about more then just saying that a player is 'bad' or 'good', you have to actually grade them (like in high school!). The bigger the highest grade, the more accurate the weighting system is.
Now, assigning weights to tactical influences is a bit more easier. Say that you have the following influences;
- Stopping goals
- Scoring goals
- Defence
- Attack
Now, create a pool of total weight (say, 1000, I like that number). These are 'tactical points' you could assign. These four influences make up a match, so you could assign 250 points to each influence. This number (250) is the universe of each influence.
The assignment of these points, per team, depends on the team's weight factors (like, do they have a good keeper?)
A keeper, for instance, weighs against the opponents keeper (and maybe also the people that are in between the keeper and the opponent, but let's keep it simple). Say the keeper of Team A weighs 80% of the total, and the keeper of Team B 20%. This rates how good they are, which is directly related to the tactical points they get. So Team A gets 80% of 250 stopping-goals-points and Team B gets 20% of those points.
The rest of the points can be assigned equally. In my example, I took only two keepers as the universe of wether a goal gets stopped or not. In reality, there could be a lot more weight factors (for you to figure out).
Once they are all divided, you can use the tactical points to make out the match. For each minute you could re-calculate the chance of winning. Each minute, you could also re-calculate the tactical influences (say another player enters the field, or a player is injured).
Yes, you will get a LOT of variables. But the more you get, the better a match plays. The more variables (or weights / counter weights) the more it feels like real life.
Best Answer
The normal approach does not use threading at all, but rather implements entities as state-machines. Then your mainloop looks like this: