Search code examples
c#multithreadingpluginsscheduled-tasksmef

Polymorphic way to call different functions with an allocated thread


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...


Solution

  • 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.