Skip to main content
Code Review

Return to Answer

replaced http://codereview.stackexchange.com/ with https://codereview.stackexchange.com/
Source Link

I will try to give you another review, once we made clear how it was designed and where is the biggest problem (in short: thread synchronization)

I will try to give you another review, once we made clear how it was designed and where is the biggest problem (in short: thread synchronization)

Source Link
user52292
user52292

I will try to give you another review, once we made clear how it was designed and where is the biggest problem (in short: thread synchronization)

What I like

One note on style: The egregious use of regions and over-commenting is company style, I'm afraid, and I can't change it. Otherwise I'm looking for advice on style, efficiency, security, and particularly type safety.

Given the sample of the code:

/// <summary>
/// Waits for multiple concurrent operations to finish before firing a callback 
/// </summary>
public class TtConcurrentSynchronizer
{
 #region Properties
 private readonly List<Callback> callbacks = new List<Callback>();
 /// <summary>
 /// The callbacks that must all be executed before the finished function is fired. 
 /// </summary>
 public IEnumerable<Action> Callbacks
 {
 get { return callbacks.Select(x => CreateCallbackAction(x)); }
 }
 #endregion Properties
 #region Private Fields
 private Action finishedFunction;
 #endregion Private Fields

I would rather use classic Doxygen (or Javadoc) style, but we are in Microsoft's world - have to follow the rules. Those #region..#endregion are nice because you can collapse them. I usually add separating comments and indent them on one tab less:

/// <summary>
/// Waits for multiple concurrent operations to finish before firing a callback 
/// </summary>
public class TtConcurrentSynchronizer
{
//====================================== depends on your prefered width, e.g. 80 or 120
#region Properties
 private readonly List<Callback> callbacks = new List<Callback>();
 /// <summary>
 /// The callbacks that must all be executed before the finished function is fired. 
 /// </summary>
 public IEnumerable<Action> Callbacks
 {
 get { return callbacks.Select(x => CreateCallbackAction(x)); }
 }
#endregion Properties
//====================================== depends on your prefered width, e.g. 80 or 120
#region Private Fields
 private Action finishedFunction;
#endregion Private Fields

Because after collapsing you will see something like this:

+ #region Properties ...
 //=======================================
+ #region Private Fields ...

And those separating comments help with scrolling while the regions are expanded.

One day you will have to manage your code. Or worse, one day somebody else will have to manage your code. I am often not documenting my code enough when I am in a hurry and need to get the job done, but once there is the time for it, I clean and comment the code (especially when my stomach is full after a lunch and my brain refuses to work :D).

What I don't like

Pardon me, but the complexity, unclear interface and all those closures:

private Action CreateCallbackAction(Callback callback)
{
 return () =>
 {
 if (callbacks.Contains(callback))

Do you really need to check if callbacks.Contains(callback)? Who can remove it before it gets executed?

What I would like to see is one simple class (this is .NET not Java, I hate connecting small pieces in javax.swing - pardon me again if you like that). Your helper is fine in doing the job, but for me, it is internal, your helper and I want the final, easy to use class:

class WorkScheduler {
 public WorkScheduler() {...} // create it to fill the jobs later
 public WorkScheduler(Action finished, params Action[] jobs) // all-in-one
 public WorkScheduler(Action finished, params WorkAndFinishedPair[] jobs) // more complex
 public void Start() // to be used with default constructor
 public event Action FinalAction; // add the action if default-constructing
 public event Action Jobs; // add jobs by +=
 public void AddJob(Action job) // the same as above with +=
 public void AddJob(Action job, Action finished) // the more complex version

All I have seen in your question was using the synchronizer together with BackgroundWorkers. The above should be the interface to connect both together. If you want to add something else, create another class using your helper.

public void Start() {
 jobCounter = jobs.Count; // atomic/interlocked downcounter
 foreach(var pair in jobs) {
 var worker = new BackgroundWorker();
 worker.DoWork += pair.JobToDo; // with possible conversion
 worker.RunWorkerCompleted += pair.FinishAction != null ? ... : ...;
 worker.RunWorkerAsync(); // we can start it now,
 // because we have initialized the counter first
 }
}

You could as well use Threads instead of BackgroundWorkers but remember what you have learned about synchronization and ISynchronizeInvoke.BeginInvoke (you cannot acces Controls from different threads).

lang-cs

AltStyle によって変換されたページ (->オリジナル) /