I've tried searching and searching for this, but maybe I'm searching for the wrong thing.
I am making a kind of controlpanel, where I can add some DLLs to be dynamically loaded.
It's no problem for me to load the DLLs and no problem to get it running - I'm using "Activator.CreateInstance" to do this at the moment.
The method is doing exactly what I want it to do.
But... I need some help to a couple of things:
1: When I'm executing the DLLs, the Form is freezing, even though I'm running it in a thread - How do I avoid this?
2: I need to be able to read the current status of the DLL, live - The DLL files are made with a Superclass, so I can read a "CurrentStatus"-property.
Is it possible to read this status of the running files?
It seems to me, as the program is waiting for the DLL to finish, and that makes it freeze.
Hopefully some of you can help me.
Thanks in advance :)
Edit: Adding some code
string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, curJob.Scheduler_JobAssemblyName + ".dll");
Assembly assembly = Assembly.LoadFile(path);
GenericSchedulerJob o = (GenericSchedulerJob)Activator.CreateInstance(Type.GetType(curJob.Scheduler_JobAssemblyName + "." + curJob.Scheduler_JobAssemblyName + ", " + curJob.Scheduler_JobAssemblyName, true));
Thread thread = new Thread(() => ExecuteDLL(ref o, "RunJob", row));
thread.Start();
while (!o.JobFinished)
{
// Do something (update status, etc.)
}
private string ExecuteDLL(ref GenericSchedulerJob job, string methodName, DataGridViewRow row)
{
string returnVal;
if (job != null)
{
// Get method
MethodInfo mi = job.GetType().GetMethod(methodName); // Get method info
if (mi != null)
{
// Let's start ...
returnVal = (string)mi.Invoke(job, null); // Execute method with parameters
// job is the SuperClass, where I can read the status from
}
else
{
returnVal = "Method <" + methodName + "> not found in class."; // Error ..
}
}
else
{
returnVal = "Class not found in external plugin."; // Error ..
}
return "";
}
Well the form is pausing because this code
while (!o.JobFinished)
{
// Do something (update status, etc.)
}
is forcing it to wait, even though the actual dll code is running on another thread.
You need to let the method finish. (Or I suppose you could use Application.DoEvents() if it doesn't leave a bad taste in your mouth.
To get the status information you should use an event that you can hook up after creating the dll. That way your app can be notified when the dll changes it's status.
Alternatively you could use a second timer and poll each running job for it's status, but events would be better.
Related
I've got a case that might be useful to analyze and extract some conclusions.
I've got a class that implements ITaskWorker, and each Task can run simultaneously with other Task connected with a scheduling engine.
Suppose Task A runs a job for object A_1 with B1...BN attributes, while for each attribute a command line runs and gives results (which is blocked until an answer is recieved from the command line process).
This means that for Task B we can schedule the same A_1 with B1...BN attributes.
For the following piece of code and explanation, could you find something that might resolve in threads interrupting each other (deadlocks, race conditions, starvation)?
How can I ensure that there isn't a multi threaded issue here?
I think starvation cannot be an issue here, unless there are a lot of tasks of the same type that other types cannot get to be done (see below explanation about the code). I don't see a case for deadlock, but there might be a race condition on mainLocaker or connectionLockers data members (because of the same variable and collection that's are used across multiple methods).
There cannot be the same key in the dictionary (I've verified that: [b_i.A_Name + "_" + b_i.B_Name] creates a unique key)
I've got this code in C#. Please notice that mainLocker and connectorsLockers is being used in several methods like doTaskOfTypeX, so several 'types' of workers might lock it in different parts of code:
private static object mainLocker = new Object();
private static Dictionary<string, object> connectionLockers = new Dictionary<string,object>();
private doTaskOfTypeA()
{
// ... initialize A from task parameters
var attibutes = getListOfAttribuesByObject(A);
bool localLocalTaken = false;
foreach (B b_i in attibutes)
{
try{
lock (mainLocker)
{
if (!typeLockers.ContainsKey(b_i.A_Name + "_" + b_i.B_Name))
{
typeLockers.Add(b_i.A_Name + "_" + b_i.B_Name, new object());
}
}
localLocalTaken = false;
Monitor.Enter(connectionLockers[b_i.A_Name + "_" + b_i.B_Name, ref localLocalTaken);
if (localLocalTaken)
{
var calcObj = callCLIProcess(); // a CMD call is in here
if (calcObj != null)
{
// do things with calcObj
}
else
{
jobResult = new ScheduleTaskResult(ResultTypes.Failed);
}
}
}
catch
{
jobResult = new ScheduleTaskResult(ResultTypes.Failed);
throw;
}
finally
{
if (localLocalTaken)
{
Monitor.Exit(connectionLockers[b_i.A_Name + "_" + b_i.B_Name]);
}
}
}
}
Actually, there is no issue here.
The [// do things with calcObj] notation had a code from an external library that didn't work too well :-)
I currently just inherited some complex code that unfortunately I do not fully understand. It handles a large number of inventory records inputting/outputting to a database. The solution is extremely large/advanced where I am still on the newer side of c#. The issue I am encountering is that periodically the program will throw an IO Exception. It doesn't actually throw a failure code, but it messes up our output data.
the try/catch block is as follows:
private static void ReadRecords(OleDbRecordReader recordReader, long maxRows, int executionTimeout, BlockingCollection<List<ProcessRecord>> processingBuffer, CancellationTokenSource cts, Job theStack, string threadName) {
ProcessRecord rec = null;
try {
Thread.CurrentThread.Name = threadName;
if(null == cts)
throw new InvalidOperationException("Passed CancellationToken was null.");
if(cts.IsCancellationRequested)
throw new InvalidOperationException("Passed CancellationToken is already been cancelled.");
long reportingFrequency = (maxRows <250000)?10000:100000;
theStack.FireStatusEvent("Opening "+ threadName);
recordReader.Open(maxRows, executionTimeout);
theStack.FireStatusEvent(threadName + " Opened");
theStack.FireInitializationComplete();
List<ProcessRecord> inRecs = new List<PIRecord>(500);
ProcessRecord priorRec = rec = recordReader.Read();
while(null != priorRec) { //-- note that this is priorRec, not Rec. We process one row in arrears.
if(cts.IsCancellationRequested)
theStack.FireStatusEvent(threadName + " cancelling due to request or error.");
cts.Token.ThrowIfCancellationRequested();
if(rec != null) //-- We only want to count the loop when there actually is a record.
theStack.RecordCountRead++;
if(theStack.RecordCountRead % reportingFrequency == 0)
theStack.FireProgressEvent();
if((rec != null) && (priorRec.SKU == rec.SKU) && (priorRec.Store == rec.Store) && (priorRec.BatchId == rec.BatchId))
inRecs.Add(rec); //-- just store it and keep going
else { //-- otherwise, we need to process it
processingBuffer.Add(inRecs.ToList(),cts.Token); //-- note that we don't enqueue the original LIST! That could be very bad.
inRecs.Clear();
if(rec != null) //-- Again, we need this check here to ensure that we don't try to enqueue the EOF null record.
inRecs.Add(rec); //-- Now, enqueue the record that fired this condition and start the loop again
}
priorRec = rec;
rec = recordReader.Read();
} //-- end While
}
catch(OperationCanceledException) {
theStack.FireStatusEvent(threadName +" Canceled.");
}
catch(Exception ex) {
theStack.FireExceptionEvent(ex);
theStack.FireStatusEvent("Error in RecordReader. Requesting cancellation of other threads.");
cts.Cancel(); // If an exception occurs, notify all other pipeline stages, then rethrow
// throw; //-- This will also propagate Cancellation, but that's OK
}
In the log of our job we see the output loader stopping and the exception is
System.Core: Pipe is broken.
Does any one have any ideas as to what may cause this? More importantly, the individual who made this large-scale application is no longer here. When I debug all of my applications, I am able to add break points in the solution and do the standard VS stepping through everything to find the issue. However, this application is huge and has a GUI that pops up when I debug the application. I believe the GUI was made for testing purposes, but it hinders me from actually being able to step through everything. However when the .exe is run from our actual job stream, there is no GUI it just executes the way it's supposed to.
The help I am asking for is 2 things:
just suggestions as to what may cause this. Could an OleDB driver be the cause? Reason I ask is because I have this running on 2 different servers. One test and one not. The one with a new OleDB driver version does not fail (7.0 i believe whereas the other where it fails is 6.0).
Is there any code that I could add that may give me a better indication as to what may be causing the broken pipe? The error only happens periodically. If I run the job again right after, it may not happen. I'd say it's 30-40% of the time it throws the exception.
If you have any additional questions about the structure just let me know.
I have a WPF project that uses background workers to interfaces with some external hardware (Test & Measure equipment, etc), write to local files, and insert data into a database as it runs. Program flow is basically sequential, the background workers in use are to keep the GUI accessible for the user, and were chosen because I haven't had issues with using them before. We take measurements, do some stuff, log, then repeat. There is a status log on the GUI that displays messages as we go.
All of this works beautifully for hours on end, however, eventually, without fail, it appears that the background worker used to write to the database never calls DoWork.
BackgroundWorker DbLogWorker = new BackgroundWorker();
...
DbLogWorker.DoWork +=
new DoWorkEventHandler(DbLogWorker_DoWork);
DbLogWorker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(
DbLogWorker_RunWorkerCompleted);
ProcessScanData is called when data has been retrieved from one of the pieces of hardware, setting a property and firing a generic property changed event
(disclaimer: some extra code everywhere as I was investigating to see whats going on):
private void Data_DataSetChanged(object pSender, EventArgs pArgs)
{
this.Test.ProcessScanData(OsaVm.Osa.Data.DataSet);
}
...
public void ProcessScanData(SortedList<double,double> pData)
{
...
RaiseLogEvent(MessageType.SystemGeneral, "Logging to database...");
DbLogWorker.RunWorkerAsync(new AsyncDbLogArgs(CurrentChannel, tempdate,
loss1, loss2, loss3,
CurrentTemperature, CurrentPressure, CurrentRoomTemp));
}
private void DbLogWorker_DoWork(object pSender, DoWorkEventArgs pArgs)
{
AsyncDbLogArgs args = (AsyncDbLogArgs)pArgs.Argument;
string filename = string.Empty;
try
{
long datakey = Db.LogScan(CurrentChannel, args.Time,
args.Temperature, args.Pressure, args.RoomTemperature,
args.Loss1, args.Loss2, args.Loss3);
filename = args.Time.ToString(FOLDER_DATETIME_FORMAT) + "_[" + datakey.ToString() + "]";
}
catch (Exception ex)
{
filename = args.Time.ToString(FOLDER_DATETIME_FORMAT) + "_{" + (fileindex++) + "}";
}
pArgs.Result = new Tuple<AsyncDbLogArgs, string>(args, filename);
}
Symptoms:
Everything works fine for anywhere between 1 hour and ~16 hours until eventually we get to a point where we see "Logging to database..." and nothing else ever happens. No more messages, no exceptions (release build on target machine), no database entry...etc. This happens consistently.
I've been scratching my head on this for a while. Any leads will help, I have some workarounds in mind but I'd really like to know whats going on so I can avoid this in the future.
Thanks
Edited back to original code...thought the most recent would help avoid some "how do you know its not firing" questions
the problem only appears when making Release build and running exe file ( not from visual studio )
in all other combination either it's running from visual studio or running exe everything works fine
I'm running Function Load using backgroundWorker
Load:
while (!Request.GAMELIST.XMLReceived) ;
GameEngine.ParseGameList( Request.GAMELIST.XML );
Request.GAMELIST.XMLReceived = false;
while loop in this fragment works like delay
it should wait till XML is received from server and then continue
but it stucks in above specified situation
if I'll put MessageBox.show("here we go"); after while loop
messageBox will not appear
but if I'll put MessageBox.show("here we go"); before while loop
application will receive data until I click messagebox ok
and then everything will work fine
here is GAMELIST class implementation
public class RequestGAMELIST
{
public string XML;
public bool XMLReceived = false;
public void ParseRequest( string request )
{
int index = request.IndexOf(':') + 2;
XML = request.Substring(index, request.Length - index);
XMLReceived = true;
}
}
please provide help if you can
this is really strange thing which I can't figure out by my self
Thanks.
Yes, this code has very good odds to hang in the Release build. The JIT optimizer doesn't know that the variable might be set to true by code outside of the method. You need to tell it that, like this:
public class RequestGAMELIST
{
public volatile bool XMLReceived = false;
// etc..
}
The volatile keyword ensures that the jitter won't store the variable value in a CPU register.
That solves your problem, it is still not the right way to do it. You should use an AutoResetEvent instead. It ensures that the thread responds to the variable change is quickly as possible. And most importantly, it lets the thread block so it doesn't burn any cpu cycles.
public class RequestGAMELIST
{
public AutoResetEvent XMLReceived = new AutoResetEvent();
public void ParseRequest( string request )
{
int index = request.IndexOf(':') + 2;
XML = request.Substring(index, request.Length - index);
XMLReceived.Set();
}
}
In your thread:
XMLReceived.WaitOne();
GameEngine.ParseGameList( Request.GAMELIST.XML );
This is a bad idea:
while (!Request.GAMELIST.XMLReceived) ;
At least you should be doing something like:
while (!Request.GAMELIST.XMLReceived) {
System.Threading.Thread.Sleep(100); // Don't hog the CPU!
}
Your program runs fine in debug mode perhaps due to certain debug routines added inside the while loop which makes it run slower...
I've made my Logger, that logs a string, a static class with a static
so I can call it from my entire project without having to make an instance of it.
quite nice, but I want to make it run in a separate thread, since accessing the file costs time
is that possible somehow and what's the best way to do it?
Its a bit of a short description, but I hope the idea is clear. if not, please let me know.
Thanks in advance!
By the way any other improvements on my code are welcome as well, I have the feeling not everything is as efficient as it can be:
internal static class MainLogger
{
internal static void LogStringToFile(string logText)
{
DateTime timestamp = DateTime.Now;
string str = timestamp.ToString("dd-MM-yy HH:mm:ss ", CultureInfo.InvariantCulture) + "\t" + logText + "\n";
const string filename = Constants.LOG_FILENAME;
FileInfo fileInfo = new FileInfo(filename);
if (fileInfo.Exists)
{
if (fileInfo.Length > Constants.LOG_FILESIZE)
{
File.Create(filename).Dispose();
}
}
else
{
File.Create(filename).Dispose();
}
int i = 0;
while(true)
{
try
{
using (StreamWriter writer = File.AppendText(filename))
{
writer.WriteLine(str);
}
break;
}
catch (IOException)
{
Thread.Sleep(10);
i++;
if (i >= 8)
{
throw new IOException("Log file \"" + Constants.LOG_FILENAME + "\" not accessible after 5 tries");
}
}
}
}
}
enter code here
If you're doing this as an exercise (just using a ready made logger isn't an option) you could try a producer / consumer system.
Either make an Init function for your logger, or use the static constructor - inside it, launch a new System.Threading.Thread, which just runs through a while(true) loop.
Create a new Queue<string> and have your logging function enqueue onto it.
Your while(true) loop looks for items on the queue, dequeues them, and logs them.
Make sure you lock your queue before doing anything with it on either thread.
sry, but you may not reinvent the wheel:
choose log4net (or any other (enterprise) logging-engine) as your logger!
Ok, simply put you need to create a ThreadSafe static class. Below are some code snippets, a delegate that you call from any thread, this points to the correct thread, which then invokes the WriteToFile function.
When you start the application that you want to log against, pass it the following, where LogFile is the filename and path of your log file.
Log.OnNewLogEntry += Log.WriteToFile (LogFile, Program.AppName);
Then you want to put this inside your static Logging class. The wizard bit is the ThreadSafeAddEntry function, this will make sure you are in the correct Thread for writing the line of code away.
public delegate void AddEntryDelegate(string entry, bool error);
public static Form mainwin;
public static event AddEntryDelegate OnNewLogEntry;
public static void AddEntry(string entry) {
ThreadSafeAddEntry( entry, false );
}
private static void ThreadSafeAddEntry (string entry, bool error)
{
try
{
if (mainwin != null && mainwin.InvokeRequired) // we are in a different thread to the main window
mainwin.Invoke (new AddEntryDelegate (ThreadSafeAddEntry), new object [] { entry, error }); // call self from main thread
else
OnNewLogEntry (entry, error);
}
catch { }
}
public static AddEntryDelegate WriteToFile(string filename, string appName) {
//Do your WriteToFile work here
}
}
And finally to write a line...
Log.AddEntry ("Hello World!");
What you have in this case is a typical producer consumer scenario - many threads produce log entries and one thread writes them out to a file. The MSDN has an article with sample code for this scenario.
For starters, your logging mechanism should generally avoid throwing exceptions. Frequently logging mechanisms are where errors get written to, so things get ugly when they also start erroring.
I would look into the BackgroundWorker class, as it allows you to fork off threads that can do the logging for you. That way your app isn't slowed down, and any exceptions raised are simply ignored.