I have a routine that creates n instances of a particular workflow and runs them each serially. How could I fire them off async?
Current p-code:
forloop
// Create
var syncEvent = new AutoResetEvent(false);
WorkflowInstance myInstance = new WorkflowInstance(new SomeWorkflow(), parameters);
// Events
// Completed
myInstance.OnCompleted = delegate(WorkflowCompletedEventArgs e) { syncEvent.Set(); };
// Unhandled Exception
myInstance.OnUnhandledException = delegate(WorkflowUnhandledExceptionEventArgs e)
{
// Message
Console.WriteLine(e.UnhandledException.ToString());
return UnhandledExceptionAction.Terminate;
};
// Aborted
myInstance.OnAborted = delegate(WorkflowAbortedEventArgs e)
{
// Message
Console.WriteLine(e.Reason);
syncEvent.Set();
};
// Run
myInstance.Run();
// Wait
syncEvent.WaitOne();
I think the easiest way to get from here to there would be just to create multiple wait handles and end with a WaitAll(). Not the most elegant solution, but it will work for you. BTW, I would recommend using a real class that holds reference to the associated wait handle and avoiding the anon methods.
List<ManualResetEvent> items = new List<ManualResetEvent>();
foreach (Type job in queue)
{
WorkflowInstance myInstance = new WorkflowInstance(job, parameters);
ManualResetEvent syncEvent = new ManualResetEvent(false);
items.Add(syncEvent);
// Completed
myInstance.OnCompleted = delegate(WorkflowCompletedEventArgs e)
{
syncEvent.Set();
};
// Unhandled Exception
myInstance.OnUnhandledException = delegate(WorkflowUnhandledExceptionEventArgs e)
{
// Message
Console.WriteLine(e.UnhandledException.ToString());
return UnhandledExceptionAction.Terminate;
};
// Aborted
myInstance.OnAborted = delegate(WorkflowAbortedEventArgs e)
{
// Message
Console.WriteLine(e.Reason);
syncEvent.Set();
};
// Run
myInstance.Run();
}
// Wait
WaitHandle.WaitAll(items.ToArray());
Use parallel framework, it will be easier.
Do you really need them running on separate threads? I'm thinking since you are using Workflow already it should be easiest to solve the problem by using workflow to 'organize your work'.
{
var ArgsToProcess = new List<string> { "arg_one", "arg_two", "arg_three" };
var delegateArg = new DelegateInArgument<string> { Name = "s" };
Activity toRun = new ParallelForEach<string>
{
Body = new ActivityAction<string>
{
Argument = delegateArg,
Handler = new Workflow1() //Plug your workflow here
{
Arg = delegateArg
}
}
};
WorkflowInvoker.Invoke(toRun, new Dictionary<string, object>
{
{"Values", ArgsToProcess}
});
}
Related
So I am trying to build a program to control a machine. Communications with said machine is via a serial port for which I have written a driver. Continuous polling to the machine is necessary for status feedback etc. In my program I have a dedicated ExecutionEngine() class to handle serial send and receive. I also need to have two separate control sequences running, which I have put into methods RunSequenceA() and RunSequenceB() respectively. During normal operation, all three methods need to run until both control sequences finish, at which point the StopSequence() method is called. My issue is that sometimes, for whatever reason, the StopSequence() method is never called, leaving my ExecutionEngine() method in an infinite loop!
Code for ExecutionEngine():
private static void ExecutionEngine()
{
// Clear both lists in case they have old data
_commandList.Clear();
_pollingList.Clear();
// Poll while user has not yet clicked "STOP"
while (!_cTokenSource.Token.IsCancellationRequested)
{
// If there are commands to be sent, send them first
if (_commandList.Count > 0)
{
Command[] tempCommandArray;
lock (_commandList)
tempCommandArray = _commandList.ToArray();
foreach (var c in tempCommandArray)
{
if (_cTokenSource.Token.IsCancellationRequested)
break;
var response = SerialDriver.ComCycle(c.CommandBytes, _serialPort);
var success = CheckErrorReturn(response, false);
if (success)
{
AddPolling(c);
RemoveCommand(c);
}
}
}
// Do polling operation on applicable controllers
if (_pollingList.Count > 0)
{
Command[] tempPollingArray;
lock (_pollingList)
tempPollingArray = _pollingList.ToArray();
foreach (var c in tempPollingArray)
{
if (_cTokenSource.Token.IsCancellationRequested)
break;
var response = SerialDriver.ComCycle(c.PollBytes, _serialPort);
var success = ProcessPollReturn(response);
if (success)
{
c.FlagDone();
RemovePolling(c);
}
}
}
if (_commandList.Count + _pollingList.Count == 0)
{
// Will get stuck here if neither list gets new items added
Console.WriteLine("Bad place");
Thread.Sleep(500);
}
}
// Cancellation has been requested
lock (_commandList)
_commandList.Clear();
lock (_pollingList)
_pollingList.Clear();
ResetTriggers();
var endCommand = new Command("GL_SYSCMD", 0);
SerialDriver.ComCycle(endCommand.CommandBytes, _serialPort);
_serialPort.Close();
_vm.SequenceRunning = false;
return;
}
Code for running sequences:
private static async Task RunSequencesAsync()
{
var taskArray = new Task[2];
var a = new Action(RunSequenceA);
var b = new Action(RunSequenceB);
taskArray[0] = Task.Run(a);
taskArray[1] = Task.Run(b);
await Task.WhenAll(taskArray).ConfigureAwait(continueOnCapturedContext: false);
// Sometimes this never fires, WHY?
UpdateStatus("All done!");
StopSequence();
}
// Run A sequence
internal static void RunSequenceA()
{
if (_sequenceA1 != null && _sequenceA1.Count > 0)
{
foreach (var s in _sequenceA1)
{
if (_cTokenSource.Token.IsCancellationRequested)
return;
s.Execute();
if (s.Reference != null && TriggerStepCompleted != null)
TriggerStepCompleted(s, EventArgs.Empty);
}
// This part always fires
Console.WriteLine("Sequence A finished");
return;
}
else
return;
}
And finally, the methods to start and stop everything:
private static async Task StartSequenceAsync()
{
_serialPort.PortName = _vm.SelectedComPort;
_serialPort.Open();
_serialPort.DiscardInBuffer();
_serialPort.DiscardOutBuffer();
// Start
_cTokenSource = new CancellationTokenSource();
_vm.SequenceRunning = true;
var taskArray = new Task[2];
taskArray[0] = Task.Run(() => ExecutionEngine());
Thread.Sleep(50);
taskArray[1] = Task.Run(() => RunSequencesAsync());
await Task.WhenAll(taskArray).ConfigureAwait(continueOnCapturedContext: false);
}
private static void StopSequence()
{
_cTokenSource.Cancel();
}
To reiterate, the problem doesn't happen every time. In fact, most times the program runs fine. It seems that problems only arise if I manually call the StopSequence() method half way through execution. Then it's 50/50 as to whether the problem shows up. I'm pretty sure my issue is threading related, but not sure exactly what is going wrong. Any help pointing me in the right direction will be greatly appreciated!
So I'm pulling in a list of items and for each item I'm creating an instance of an object to run a task on that item. All the objects are the same, they updated based off of a received message every three seconds. This update does not all occur at once though, sometimes it takes 3.1 seconds, etc. This is data I need to serialize in XML once it all exists so I'm looking for a way to see when its all done.
I've explored tasks in .net 4.6 but that initiates a task and it reports complete and then to run again the task class would initiate it again but in my case that won't work because each instance stays alive and initiates itself when a new message comes in.
What is the best way to have it report it reached the last line of code and then look at a list of these instances and say when all of them show as complete then run task to serialize?
I've included code below of the instance that is running.
private void OnMessageReceived(object sender, MessageReceivedEventArgs e)
{
var eventArgs = new CallDataReceivedEventArgs();
this.OnCallDataReceived(eventArgs);
try
{
List<Tuple<String, TimeSpan>> availInItems = new List<Tuple<string, TimeSpan>>();
List<Tuple<string, int, TimeSpan, string, string, string>> agentlist = new List<Tuple<string, int, TimeSpan, string, string, string>>();
if (e == null)
{
return;
}
List<TimeSpan> listOfTimeSpans = new List<TimeSpan>();
if (e.CmsData != null)
{
#region Gathering Agent Information
// Create a list of all timespans for all _agents in a queue using the property AgentTimeInState
foreach (var item in e.CmsData.Agents)
{
//AgentData = new ScoreBoardAgentDataModel(AgentName, AgentExtension, AgentTimeInState, AgentAuxReason, AgentId, AgentAdcState);
_agentData.AgentName = item.AgName;
_agentData.AgentExtension = item.Extension;
_agentData.AgentAuxReason = item.AuxReasonDescription;
_agentData.AgentId = item.LoginId;
_agentData.AgentAcdState = item.WorkModeDirectionDescription;
_agentData.AgentTimeInState = DateTime.Now - item.DateTimeUpdated;
_agentData.TimeSubmitted = DateTime.Now;
agentlist.Add(Tuple.Create(_agentData.AgentName, _agentData.AgentExtension, _agentData.AgentTimeInState, _agentData.AgentId, _agentData.AgentAcdState, _agentData.AgentAuxReason));
if (_agentData.AgentAcdState == "AVAIL")
{
listOfTimeSpans.Add(_agentData.AgentTimeInState);
availInItems.Add(Tuple.Create(_agentData.AgentName, _agentData.AgentTimeInState));
}
availInItems.Sort((t1, t2) => t1.Item2.CompareTo(t2.Item2));
}
var availInAgents =
agentlist
.Where(ag => ag.Item5 == "AVAIL")
.ToList();
availInAgents.Sort((t1, t2) =>
t1.Item3.CompareTo(t2.Item3));
var max3 = availInAgents.Skip(availInAgents.Count - 3);
max3.Reverse();
_agents.AgentsOnBreak = 0;
foreach (var agent in agentlist)
{
if (!string.IsNullOrEmpty(agent.Item6) && agent.Item6.StartsWith("Break"))
{
_agents.AgentsOnBreak++;
}
}
_agents.AgentsOnLunch = 0;
foreach (var agent in agentlist)
{
//If the current agent's aux reason is Lunch
if (!string.IsNullOrEmpty(agent.Item6) && agent.Item6.StartsWith("Lunch"))
{
//add one to agentsonlunch
_agents.AgentsOnLunch++;
}
}
_agents.NextInLine = string.Empty;
foreach (var agent in max3.Reverse())
{
//assign agent to NextInLine and start a new line
_agents.NextInLine += agent.Item1 + Environment.NewLine;
//reverse NextInLine
_agents.NextInLine.Reverse();
}
_agents.TimeSubmitted = DateTime.Now;
#endregion
#region Gathering Skill Information
_skillData.OldestCall = e.CmsData.Skill.OldestCall;
_skillData.AgentsStaffed = e.CmsData.Skill.AgentsStaffed;
_skillData.AgentsAuxed = e.CmsData.Skill.AgentsInAux;
_skillData.AgentsAvailable = e.CmsData.Skill.AgentsAvailable;
_skillData.AgentsOnCalls = e.CmsData.Skill.AgentsOnAcdCall;
_skillData.CallsWaitingInQueue = e.CmsData.Skill.InQueueInRing;
_skillData.Asa = e.CmsData.Skill.AnswerTimePerAcdCall;
_skillData.TimeSubmitted = DateTime.Now;
_skillData.EstimatedHoldTimeLow = e.CmsData.Skill.ExpectedWaitTimeLow;
_skillData.EstimatedHoldTimeMedium = e.CmsData.Skill.ExpectedWaitTimeMedium;
_skillData.EstimatedHoldTimeHigh = e.CmsData.Skill.ExpectedWaitTimeHigh;
#endregion
}
}
catch (Exception ex)
{
_logger.Info(ex.Message, ex);
}
}
With tasks you can start many at the same time and wait for them all to finish like this:
var taskList = new List<Task>();
foreach (var thingToDo in work)
{
taskList.Add(thingToDo.StartTask());
}
Task.WaitAll(taskList.ToArray());
This way you can run everything in parallel and wont get after the last line until everything is done.
Edit following your comment
You can embed your work in a task with this:
public async Task DoWork()
{
var taskList = new List<Task>();
foreach (var thingToDo in work)
{
taskList.Add(thingToDo.StartTask());
}
await Task.WhenAll(taskList.ToArray());
}
I need to simulate an existing WF process "on dry", meaning it was persisted to db and now I resume it to collect various "what-if" data.
After it completes, I Abort() it, but it leaves WF instance void.
app = new WorkflowApplication(flowChart);
app.Load("guid");
app.InstanceStore = CreateInstanceStore();
var evt = new AutoResetEvent(false);
app.PersistableIdle = e =>
{
evt.Set();
return PersistableIdleAction.None;
};
app.ResumeBookmark("bookmark");
evt.WaitOne();
app.Abort();
private SqlWorkflowInstanceStore CreateInstanceStore()
{
SqlWorkflowInstanceStore instanceStore = new SqlWorkflowInstanceStore(connectionString)
{
InstanceCompletionAction = InstanceCompletionAction.DeleteNothing
};
var instanceHandle = instanceStore.CreateInstanceHandle();
var view = instanceStore.Execute(instanceHandle,
new CreateWorkflowOwnerCommand(), TimeSpan.FromSeconds(10));
instanceHandle.Free();
instanceStore.DefaultInstanceOwner = view.InstanceOwner;
return instanceStore;
}
I need to "undo" any changes the wf state may have done to persistent store.
Any suggestions?
I am using following code to initiate Jobs.
List<Thread> threads = new List<Thread>();
List<Job> foundedJobs = new List<Job>();
public void StartAllJobs()
{
try
{
if (CanExecuteJobs == false) return;
var jobs = GetAllTypesImplementingInterface(typeof(Job)); // get all job implementations of this assembly.
var enumerable = jobs as Type[] ?? jobs.ToArray();
if (jobs != null && enumerable.Any()) // execute each job
{
Job instanceJob = null;
var index = 0;
foreach (var job in enumerable)
{
if (IsRealClass(job)) // only instantiate the job its implementation is "real"
{
try
{
instanceJob = (Job)Activator.CreateInstance(job); // instantiate job by reflection
foundedJobs.Add(instanceJob);
var thread = new Thread(new ThreadStart(instanceJob.ExecuteJob))
{
IsBackground = true,
Name = "SID" + index
}; // create thread for this job execution method
thread.Start();// start thread executing the job
threads.Add(thread);
index++;
}
catch (Exception ex)
{
App.Logger.Error(ex);
}
}
}
}
}
catch (Exception ex)
{
App.Logger.Error(ex);
}
}
How I can access Job class from thread later in some other class?
What I mean is that we create Thread like this
var thread = new Thread(new ThreadStart(instanceJob.ExecuteJob))
{
IsBackground = true,
Name = "SID" + index
};
So can I use thread somehow to access instanceJob?
Thanks!
Threads aren't really associated with any particular class, and its ThreadStart is not exposed, so given a thread, there isn't really a class context to be extracted.
Instead, what it looks like is that you need to create some dictionary with Thread as key, and your job as value. Then, given a Thread instance you can query the dictionary for the associated job instance.
Dictionary<Thread, Job> threads = new Dictionary<Thread, Job>();
List<Job> foundedJobs = new List<Job>();
public void StartAllJobs()
{
try
{
if (CanExecuteJobs == false) return;
var jobs = GetAllTypesImplementingInterface(typeof(Job)); // get all job implementations of this assembly.
var enumerable = jobs as Type[] ?? jobs.ToArray();
if (jobs != null && enumerable.Any()) // execute each job
{
Job instanceJob = null;
var index = 0;
foreach (var job in enumerable)
{
if (IsRealClass(job)) // only instantiate the job its implementation is "real"
{
try
{
instanceJob = (Job)Activator.CreateInstance(job); // instantiate job by reflection
foundedJobs.Add(instanceJob);
var thread = new Thread(new ThreadStart(instanceJob.ExecuteJob))
{
IsBackground = true,
Name = "SID" + index
}; // create thread for this job execution method
thread.Start();// start thread executing the job
threads.Add(thread, job);
index++;
}
catch (Exception ex)
{
App.Logger.Error(ex);
}
}
}
}
}
catch (Exception ex)
{
App.Logger.Error(ex);
}
}
in other places where you need to get job for a thread do:
job = threads[myThreadInstance]; // Where myThreadInstance is an instance of Thread class...
I would recommend using .NET 4's Task<T> to hold this, instead of using Thread. You can then directly return the Job within the Task<Job>:
public List<Task<Job>> StartAllJobs()
{
if (CanExecuteJobs == false) return;
var jobs = GetAllTypesImplementingInterface(typeof(Job)); // get all job implementations of this assembly.
return jobs.Where(IsRealClass)
.Select(job => (Job)Activator.CreateInstance(job))
.Select(job => Task.Factory.StartNew(() => { job.ExecuteJob(); return job; }))
.ToList();
}
This will give you a list of Tasks that will be completed once the job's ExecuteJob method is done, and the Task.Result will be the job that executed.
I am interested in timing my function calls to database + other functions to build some metrics for my application's performance. I used Stopwatch and a metrics object, but it doesn't seem to consistently give correct values. Sometimes the elapsed time for calling a function is exactly the same for all the calls which is unrealistic...
I have discovered that the reason for the problem is due to the Metrics object properties values. The values of one Metrics object get overwritten when other instances of the Metrics generated by other threads are assigned values. It seems like the properties values are per reference although a new instance is created by each thread.
What's the best approach to achieve uniqueness in an object shared by multiple threads?
Code below:
private Metrics Metrics;
private Stopwatch Stopwatch;
private int DegreeOfParallelism { get { return Convert.ToInt32(ConfigurationManager.AppSettings["DegreeOfParallelism"].ToString()); } }
var lOptions = new ParallelOptions() { MaxDegreeOfParallelism = DegreeOfParallelism };
Parallel.ForEach(RequestBag, lOptions, (lItem, loopState) =>
{
if (!string.IsNullOrEmpty(lItem.XmlRequest))
{
try
{
Metrics = new Metrics();
Stopwatch = new Stopwatch();
Stopwatch.Start();
ObjRef = new Object();
lItem.XmlRequest = ObjRef.GetDecision(Username, Password);
Stopwatch.Stop();
Metrics.ElapsedTime = string.Format("{0:0.00}", Stopwatch.Elapsed.TotalSeconds);
Stopwatch.Restart();
if (!string.IsNullOrEmpty(DBConnectionString))
{
DataAccess = new DataAccess2(DBConnectionString);
DataAccess.WriteToDB(lItem.XmlRequest);
}
Stopwatch.Stop();
Metrics.DbFuncCallTime = string.Format("{0:0.00}", Stopwatch.Elapsed.TotalSeconds);
}
catch (Exception pEx)
{
KeepLog(pEx);
Metrics.HasFailed = true;
}
finally
{
ProcessedIdsBag.Add(lItem.OrderId);
Metrics.ProcessedOrderId = lItem.OrderId;
Metrics.DegreeOfParallelism = DegreeOfParallelism;
Metrics.TotalNumOfOrders = NumberOfOrders;
Metrics.TotalNumOfOrdersProcessed = ProcessedIdsBag.Count;
pBackgroundWorker.ReportProgress(Metrics.GetProgressPercentage(NumberOfOrders, ProcessedIdsBag.Count), Metrics);
RequestBag.TryTake(out lItem);
}
}
});
Any help will be very much appreciated.
Thanks,
R
What it seems that you want to do is create a metrics object for each iteration, and then aggregate them at the end:
private ConcurrentBag<Metrics> allMetrics = new ConcurrentBag<Metrics>();
private int DegreeOfParallelism { get { return Convert.ToInt32(ConfigurationManager.AppSettings["DegreeOfParallelism"].ToString()); } }
var lOptions = new ParallelOptions() { MaxDegreeOfParallelism = DegreeOfParallelism };
Parallel.ForEach(RequestBag, lOptions, (lItem, loopState) =>
{
if (!string.IsNullOrEmpty(lItem.XmlRequest))
{
try
{
var Metrics = new Metrics();
var Stopwatch = new Stopwatch();
Stopwatch.Start();
ObjRef = new Object();
lItem.XmlRequest = ObjRef.GetDecision(Username, Password);
Stopwatch.Stop();
Metrics.ElapsedTime = string.Format("{0:0.00}", Stopwatch.Elapsed.TotalSeconds);
Stopwatch.Restart();
if (!string.IsNullOrEmpty(DBConnectionString))
{
DataAccess = new DataAccess2(DBConnectionString);
DataAccess.WriteToDB(lItem.XmlRequest);
}
Stopwatch.Stop();
Metrics.DbFuncCallTime = string.Format("{0:0.00}", Stopwatch.Elapsed.TotalSeconds);
}
catch (Exception pEx)
{
KeepLog(pEx);
Metrics.HasFailed = true;
}
finally
{
ProcessedIdsBag.Add(lItem.OrderId);
Metrics.ProcessedOrderId = lItem.OrderId;
Metrics.DegreeOfParallelism = DegreeOfParallelism;
Metrics.TotalNumOfOrders = NumberOfOrders;
Metrics.TotalNumOfOrdersProcessed = ProcessedIdsBag.Count;
pBackgroundWorker.ReportProgress(Metrics.GetProgressPercentage(NumberOfOrders, ProcessedIdsBag.Count), Metrics);
RequestBag.TryTake(out lItem);
allMetrics.add(Metrics);
}
}
});
// Aggregate everything in AllMetrics here
You need to change the scope of your Stopwatch and Metrics variables.
Currently, each thread shares the same Metrics variable. As soon as a thread enters the try block, it creates a new instance of Metrics (correctly), but puts it a shared variable (incorrectly). All other threads will see that new instance when reading the shared variable until the next thread comes along and start the whole process over.
move
private Metrics Metrics;
private Stopwatch Stopwatch;
to just inside your loop
Parallel.ForEach(RequestBag, lOptions, (lItem, loopState) =>
{
private Metrics Metrics;
private Stopwatch Stopwatch;
...
This will give each iteration through the loop it's own variable in which to store it's own instance of the the object.