Polymorphic way to call different functions with an allocated thread - c#

I have a class with a set of functions that differ in the number of parameters and the parameter types. I've been trying to figure out a way to invoke a call to a desired function inside an allocated thread.
What's a simple way of doing this? I've looked into System.Action, but that requires the parameters to be known. I've also gone over TaskFactory and TPL, but from the examples I've seen, I can't put together the solution in my head.
What I want to eventually do is queue up jobs that will be executed by a limited number of threads. The jobs performed are simple HTTP requests.
I feel like this has been done before and has a simple solution, but it has eluded me for weeks. I'm hoping for an elegant way of doing it instead of a lot of complex code.
I'm also trying to implement MEF plugins to make matters worse.
public bool AddThreads(int numOfThreads)
{
try
{
// Exit if no plugin type is set
if (PluginType == "") return false;
int totalThreads = numOfThreads + threads.Count;
for (int i = threads.Count; i < totalThreads; i++)
{
// Create an instance of the MEF plugin
var task = PluginHandler.CreateInstance(PluginType);
threads.Add(task);
task.ThreadId = i;
task.OnStatusChange += new TaskerPlugin.EventHandler(ChangeStatus);
task.OnActionComplete += new TaskerPlugin.EventHandler(ReportComplete);
task.OnActionSuccess += new TaskerPlugin.EventHandler(ReportSuccess);
task.OnActionFailure += new TaskerPlugin.EventHandler(ReportFailure);
task.OnActionAttempt += new TaskerPlugin.EventHandler(ReportAttempt);
task.OnActionError += new TaskerPlugin.EventHandler(ReportError);
task.OnActionCancelled += new TaskerPlugin.EventHandler(ReportCancellation);
task.OnActionBegin += new TaskerPlugin.EventHandler(ReportStartOfTask);
task.OnActionEnd += new TaskerPlugin.EventHandler(ReportEndOfTask);
// Do work from plugin
// This is where I'd like to call different
// functions possibly inside Start()
task.Start();
}
return true;
}
catch (Exception)
{
return false;
}
}
Current code calling the function:
private void CreateThreads(int threadCount)
{
// taskMan is a class variable to queue more jobs in the future
taskMan = new TaskManager(PLUGIN_FOLDER)
{
PluginType = PLUGIN_TYPE,
UseProxies = (_config.IpSolution == "Proxies" || _config.IpSolution == "Proxy URL")
};
taskMan.AddThreads(threadCount);
}
I want to eventually just call a function to add a job to it with a predefined number of threads:
private void AddJob(string pluginName, string methodName, List<string> args)
I'd prefer not just using a string list to put all of my arguments in, but I don't really know of another way of doing it. Maybe a list of objects which I then cast later? Both these ideas are very messy...

I am assuming that AddJob is the overloaded method that you need to call with different parameters.
You might have to tweak your PluginHandler.CreateInstance(PluginType) method to do something like this while creating the task, this would allow you to execute any method you need in the task that you create..
Task task = new Task(() =>
{
classInstance.YourMethod("some param1", some other param2));
}
Further with some reflection..
var classInstance = new YourClass();
Type type = classInstance.GetType();
MethodInfo methodInfo = type.GetMethod("AddJob");
object[] parametersArray = new object[] { "some param1", "some parma2" };
methodInfo.Invoke(methodInfo, parametersArray);
and finally,
Task task = new Task(() =>
{
var classInstance = new YourClass();
Type type = classInstance.GetType();
MethodInfo methodInfo = type.GetMethod("AddJob");
object[] parametersArray = new object[] { "some param1", "some parma2" };
methodInfo.Invoke(classInstance, parametersArray);
}
In case the AddJob method is present in the current class itself, there could be little changes to the code.

Related

Does this result in a lock at execution time?

I have this method here inside a WCF service call, that checks a Dictionary and then adds to it if there is no key present. I create a Delegate inside a lock block, wondering if that would be a problem.
if (_delegates.ContainsKey(req.OperationName))
{
return _delegates[req.OperationName](req);
}
else
{
lock (_syncRoot)
{
if (_delegates.ContainsKey(req.OperationName) == false)
{
System.Reflection.MethodInfo mi = GetType().GetMethod(req.OperationName, new Type[] { typeof(DataRequestContext) });
if (mi != null)
{
InvokeDelegate d = new InvokeDelegate((r) => { return mi.Invoke(this, new object[] { r }); });
_delegates.Add(req.OperationName, d);
}
else
{
throw new Exception("Unsupported operation: " + req.OperationName);
}
}
return _delegates[req.OperationName](req);
}
The code above is executed like this:
var del = GetDelegate( request);
del(request);
The first call will add to the Dictionary, subsequent calls will not need to add to the static Dictionary but the Delegate will be executed every time the code is run.
My brain says that this code segment will NOT block, am I correct?
InvokeDelegate d = new InvokeDelegate((r) => { return mi.Invoke(this, new object[] { r }); });
I cannot test it and generate the lock condition so I need to ask:
The code that actually gets executed in the real world scenario is below, the parameter req is an object that contains runtime values I use to execute:
public AES.Cloud.Common.DataDictionary GetConditionAndItems(AES.Cloud.Common.DataRequestContext req)
{
List<ConditionItem> items = null;
List<MiscItem> miscitems = null;
var cond = GetConditionAndItems(req.GetValue<Guid>("conditionId"), out items, out miscitems);
AES.Cloud.Common.DataDictionary dd = new Common.DataDictionary();
dd.AddList("items", items);
dd.AddList("miscItems", miscitems);
dd.AddObject("Condition", cond);
return dd;
}
Any thread reaching a lock statement will wait for any existing locks (on the same object) to be released before executing the code within the lock.
If your code that invokes the delegate is not encapsulated within a lock, it will not await a lock release to execute..

Dynamic length of generic parameters

I've written a method that gets an array of objects, the types of the array contents as generics and tries to cast each type to the delivered generic:
public static void GetMultipleObjectsFromParameters<T, U>(
object parameterArray, out T parameter1, out U parameter2)
{
parameter1 = default(T);
parameter2 = default(U);
try
{
object[] arr = parameterArray as object[];
if (arr == null)
{
Debug.WriteLine("array not valid");
return;
}
if (arr.Length != 2)
{
Debug.WriteLine("arr.Length != 2");
return;
}
parameter1 = (T)arr[0];
parameter2 = (U)arr[1];
}
catch (Exception ex)
{
Debug.Write(ex);
}
}
I thought this method could be very useful if I use a BackgroundWorker and want to deliver more than one argument of different types (e.g. first parameter as string, second as integer...).
Now I've just wondered if there is a way to write this method without forcing a fixed size of parameters. This would prevent me from writing a method like this for each count of parameters.
I hope the question was comprehensible. Is there an easy approach? Thank you for your help.
EDIT: This answer addresses the original question rather than the motivation behind it. In terms of passing work to a BackgroundWorker etc, using a lambda expression makes perfect sense.
Now I've just wondered if there is a way to write this method without forcing a fixed size of parameters.
Nope, I'm afraid not. What you're looking for is higher order types, I think1 - but they don't exist in .NET (which is why there are so many "generic arity overloads" for Func<>, Tuple<>, Action<> etc).
1 I don't know much about these things myself, to be honest. Joe Duffy has a blog post which blows my mind somewhat, but which may be useful.
if there is a way to write this method without forcing a fixed size of parameters
You don't need to write this method with variable size of parameters. You need Tuple:
var myParams = Tuple.Create(100, "Hello, world!");
var worker = new BackgroundWorker();
worker.DoWork += (sender, args) =>
{
var arg = (Tuple<int, string>)args.Argument;
if (arg.Item2.Length > 5)
{
var foo = arg.Item1 + 200;
}
// etc ...
};
worker.RunWorkerAsync(myParams);
or lambdas with closures:
var someIntParam = 100;
var someStringParam = "Hello, world!";
var worker = new BackgroundWorker();
worker.DoWork += (sender, args) =>
{
if (someStringParam.Length > 5)
{
var foo = someIntParam + 200;
}
// etc ...
};
worker.RunWorkerAsync();
It depends on how do you use BackgroundWorker in your real code.

Calling methods selected in a ListBox

I have an issue with a Windows Forms application that I am creating. The application is supposed to be an integration testing application. Essentially, it's supposed to test the methods that utilize lots of web services in one of my classes. I am loading the methods from the class I want to test via reflection, and am doing so like this:
private List<string> GetMethods(Type type)
{
return (from method in type.GetMethods() where method.IsPublic &&
method.ReturnType == typeof(void) select method.Name).ToList();
}
This returns a list of the methods from that class that have been created to test the web services and places them in a ListBox where the user can select as many methods as he/she likes. My confusion comes in here. What I would like to do is get the methods selected by the user and execute the corresponding method X amount of times (there is a text box for entering the number of times to execute a method on the form as well). I can't figure out how to execute these methods based on the name of the method I got through reflection. I've tried something like this, but I know it's not right:
private void RunMethods(Type type)
{
var tester = new ClassToTest();
foreach(var item in lstMethodList.SelectedItems)
{
foreach(var method in type.GetMethods())
{
if(String.Equals(item.ToString(), method.Name))
{
ThreadStart ts = new ThreadStart(method.Name);
Thread thread1 = new Thread(ts);
thread1.Start();
}
}
}
}
This won't even compile, as a ThreadStart requires a method name as a parameter. Is there any way that this is possible to do? Maybe I'm going about it wrong logically, but I'd like to create a thread for each method that needs to be run and execute that method however many times the user specifies. This is supposed to be a way of doing integration testing along with some load testing to see what the web service can handle.
You can use something like this to get the methods you want:
private List<MethodInfo> GetMethods(Type type)
{
return (from method in type.GetMethods()
where method.IsPublic &&
method.ReturnType == typeof(void)
select method).ToList();
}
Then if you want to call the methods in separate threads you would write (will work only if the methods are static):
foreach(MethodInfo mi in GetMethods(SomeType) {
MethodInfo tempMi = mi;
Action myAction = (Action) Delegate.CreateDelegate(typeof(Action), tempMi);
ThreadStart ts = new ThreadStart(myAction);
Thread thread1 = new Thread(ts);
thread1.Start();
}
Or you would write (only if the methods have strictly no parameters, beware of inherited methods which may take parameters!):
foreach (MethodInfo mi in GetMethods(type))
{
MethodInfo tempMi = mi; //modified closure
object o = Activator.CreateInstance( type );
Action myAction = delegate() { tempMi.Invoke(o, null); };
ThreadStart ts = new ThreadStart(myAction);
Thread thread1 = new Thread(ts);
thread1.Start();
}
If the methods take parameters, you'd have to pass an array of object( object [] { ... } ) instead of null (in the Invoke method called on the current MethodInfo) accordingly; with of course corrects objects in the array.
It would actually be better if you take a List of Thread and add a new Thread in it for each MethodInfo in the list so you can keep control on them afterwards (like if you want to stop one). A HashMap would also be a good choice, the key being the MethodInfo or the Action and the value being the associated Thread.
You can create an instance of your class using Activator.
Then you can call one of its methods using Invoke.
Something like this should work:
private void RunMethods(Type type)
{
foreach( var item in lstMethodList.SelectedItems )
{
foreach( var method in type.GetMethods() )
{
if( String.Equals( item.ToString(), method.Name))
{
MethodInfo capturedMethod = method;
var t = new Thread( () => ThreadMain( type, capturedMethod ) );
t.Start();
}
}
}
}
static void ThreadMain( Type type, MethodInfo mi )
{
object o = Activator.CreateInstance( type );
object[] parameters = new object[] { };
mi.Invoke( o, parameters );
}
I've assumed the class being tested has a parameter-less constructor.
You can implement a private dictionary which holds the MethodInfos and Method name.
private Dictionary<string, MethodInfo> methodList;
private List<string> GetMethods(Type type)
{
methodList = new Dictionary<string, MethodInfo>();
type.GetMethods().Where(m=>m.IsPublic && m.ReturnType.Equals(typeof(void))).ToList().ForEach(m=>
methodList.Add(m.Name,m)
);
return methodList.Keys.Select(k => k).ToList();
}
on selecting the dropdown, you can find the method in the dictionary and execute it.

How to invoke a delegate with a null parameter?

I get a null exception if I try to pass a null parameter to a delegate during an invoke. Here's what the code looks like:
public void RequestPhoto()
{
WCF.Service.BeginGetUserPhoto(Contact.UserID,
new AsyncCallback(RequestPhotoCB), null);
}
public void RequestPhotoCB(IAsyncResult result)
{
var photo = WCF.Service.EndGetUserPhoto(result);
UpdatePhoto(photo);
}
public delegate void UpdatePhotoDelegate(Binary photo);
public void UpdatePhoto(Binary photo)
{
if (InvokeRequired)
{
var d = new UpdatePhotoDelegate(UpdatePhoto);
Invoke(d, new object[] { photo });
}
else
{
var ms = new MemoryStream(photo.ToArray());
var bmp = new Bitmap(ms);
pbPhoto.BackgroundImage = bmp;
}
}
The problem is with the line:
Invoke(d, new object[] { photo });
If the variable "photo" is null. What is the correct way to pass a null parameter during an invoke?
Thanks!
Just for the benefit of others, you can pass null arguments to delegates (if the type allows it? Clarification needed here). In your case, IAsyncResult will allow it.
As for the debugging, the exception occurs on Invoke because you are debugging on a given Thread A, the exception occurs on Thread B. You can breakpoint multiple threads. Breakpoint the Thread B code and you will see the exception closer to or on the source.
Notice though that your debugger will jump around if multiple threads are running code at the same time. Debugging in multiple threads is always at least a little tricky, but satisfying when you solve the problems.
You could also further improve your answer code to check the null before it checks the InvokeRequired, as this is thread-independent to your logic (your code checks it just prior to use, after Invoking). This will save pushing the Invoke onto the message pump (assuming WinForms).
OK I figured it out. The problem was NOT with passing the null parameter to the delegate like I thought. The problem was with the delegate executing it was causing a null exception on the line:
var ms = new MemoryStream(photo.ToArray());
I didn't realize the problem was there because it was crashing on the Invoke line.
So I changed the UpdatePhoto method as follows:
public void UpdatePhoto(Binary photo)
{
if (InvokeRequired)
{
var d = new UpdatePhotoDelegate(UpdatePhoto);
Invoke(d, new object[] { photo});
}
else
{
if (photo != null)
{
var ms = new MemoryStream(photo.ToArray());
var bmp = new Bitmap(ms);
pbPhoto.BackgroundImage = bmp;
}
}
}
And all is well!

C# lambda ref out

I'm trying to do this, but it doesn't work. Some suggestions?
int test_i = 0;
DoSomethingThatTakesAgesAndNeedsToUpdateUiWhenFinished(test_i);
test_i <- still is 0 and not 3!!!
public void DoSomethingThatTakesAgesAndNeedsToUpdateUiWhenFinished(int i)
{
DisableUi();
m_commandExecutor.ExecuteWithContinuation(
() =>
{
// this is the long-running bit
ConnectToServer();
i = 3; <--------------------------
// This is the continuation that will be run
// on the UI thread
return () =>
{
EnableUi();
};
});
}
Why I can't set test_i to 3? I also tried ref and out, but it doesn't work.
What can I do to fix it?
EDIT
I've tried this, but ouside of this method dataSet still is empty.
public static void Select(DataGridView dataGridView, ref DataSet dataSet, params object[] parameters)
{
var _dataSet = dataSet;
AsyncCommandExecutor commandExecutor = new AsyncCommandExecutor(System.Threading.SynchronizationContext.Current);
commandExecutor.ExecuteWithContinuation(
() =>
{
// this is the long-running bit
_dataSet = getDataFromDb(parameters);
// This is the continuation that will be run on the UI thread
return () =>
{
dataGridView.DataSource = _dataSet.Tables[0].DefaultView;
};
});
dataSet = _dataSet;
}
When passing the variable using the ref keyword, you can't use it inside a lambda expression. Try using a local variable inside the lambda and assign the ref variable outside it, if possible (somewhat simplified example):
private static void Main(string[] args)
{
int i = 0;
DoSomethingThatTakesAgesAndNeedsToUpdateUiWhenFinished(ref i);
Console.WriteLine(i);
}
public static void DoSomethingThatTakesAgesAndNeedsToUpdateUiWhenFinished(ref int i)
{
int temp = i;
Thread t = new Thread(() =>
{
temp = 3; // assign the captured, local variable
});
t.Start();
t.Join();
i = temp; // assign the ref parameter
}
Update
In response to the updated answer: your problem is that _dataSet inside the lambda expression is not the same variable as dataSet outside the lambda expression. What you could do is the following:
class DataSetContainer
{
public DataSet DataSet { get; set; }
}
Now we have a reference type with a property that we can safely modify inside the lambda expression:
public static void Select(DataGridView dataGridView,
DataSetContainer dataSetContainer,
params object[] parameters)
{
AsyncCommandExecutor commandExecutor = new AsyncCommandExecutor(System.Threading.SynchronizationContext.Current);
commandExecutor.ExecuteWithContinuation(
() =>
{
// this is the long-running bit
dataSetContainer.DataSet = getDataFromDb(parameters);
// This is the continuation that will be run on the UI thread
return () =>
{
dataGridView.DataSource = _dataSet.Tables[0].DefaultView;
};
});
}
}
In the above code, the lambda expression will update the DataSet property of the DataSetContainer instance that is passed to the Select method. Since you are not modifying the passed argument itself but only a member of that instance there is no need for the ref keyword, and we also get around the closure issue.
Update 2
And now when I switched on my brain, I realize that the Select method makes an asynchronous call. It is quite likely as the code looks that the last line is the Select method will be executed long before _dataSet is being assigned, and as a result it will be null. To get around this you probably want to look into using some sort of signaling mechanism (such as ManualResetEvent or AutoResetEvent) to know when the assignment is done.
The i variable in the lambda expression refers to the parameter i of the method. As a workaround, you can make it to refer to a global variable (dirty solution).
By the way, you can't capture ref and out variables in lambdas, but you can have them as parameters. You need to change the signature of your delegate and the implementation of the method receiving the delegate, which might not be suitable:
(out int i) => { i = 10; }

Categories

Resources