1

Somewhere on my main thread i make a new thread which creates an object that is only allowed to be instantiated once through the entire application time.

Further down my main thread i have a function that makes use of this object that is also a global variable by the way.

So i wish to run this function on the same thread that the object was created.

Question is how can i achieve this when it is the Main threads decision when this function should be called?

// global variable
 private static IWebDriver driver;

// Main Thread thread creation
Thread thread = new Thread(() => 
{  
   driver = new ChromeDriver(@"myPath");
});
thread.Start();

// some click event on main thread 
myFunctionUsingDriverObject(); 

So i need some way to tell the function to run on the same thread as driver was created. Usually you would use the methodInvoker but the IWebDriver does not have such a method. So is there another way i can invoke the function into the thread of driver?

If anyone is wondering why i want to do this. Then it is because the UI is run on the Main Thread and then the function will freeze the UI until completion if it is also run on the main thread.

Nulle
  • 1,301
  • 13
  • 28
  • Why not create a new thread? Simplest and uglies way is to create a new thread and put a `while (true)` to check if the `driver` is still `null`, then continue with `myFunctionUsingDriverObject()`? – Soroush Falahati Nov 22 '17 at 16:38
  • @SoroushFalahati I believe OP wants to implement functionality similar to "UI thread" and `Dispatcher.Invoke` in WPF where some objects must be only accessed on "UI thread". This is quite common requirement as some objects (particularly Windows UI once) may behave incorrectly when used from thread that is different from one that created them. – Alexei Levenkov Nov 22 '17 at 16:40
  • I think you are looking for https://www.bing.com/search?q=c%23+schedule+task+particular+thread like https://stackoverflow.com/questions/30719366/run-work-on-specific-thread. (May be even duplicate) – Alexei Levenkov Nov 22 '17 at 16:42
  • @AlexeiLevenkov, I am not sure. He says that he cant create a new `ChromeDriver` as it blocks the main thread. Then he wants to run `myFunctionUsingDriverObject()` with the resulting `ChromeDriver` from the other thread. – Soroush Falahati Nov 22 '17 at 16:42
  • @SoroushFalahati yes, exactly what I tried to say - replicate "UI thread" + `Invoke` functionality with custom thread "My Special Thread" + `MySpecialThreadInvoke`... – Alexei Levenkov Nov 22 '17 at 16:45
  • It is not chromeDriver that blocks UI thread. It is the function that blocks the UI thread. However The instantiation of ChromeDriver and the call to the function has to be performed on the same thread or else you will get a SystemInvalidOperationException due to Cross-Thread operation. Remember ChromeDriver can only be instantiated once through the entire application time. – Nulle Nov 22 '17 at 16:48

2 Answers2

2

Add a reference to the WindowsBase.dll and write this code:

        private static IWebDriver driver;

        private static Dispatcher dispatcher = null;

        AutoResetEvent waitHandle = new AutoResetEvent(false);            
        var thread = new Thread(() =>
        {
            dispatcher = Dispatcher.CurrentDispatcher;
            waitHandle.Set();
            Dispatcher.Run();
        });
        thread.Start();
        waitHandle.WaitOne();

        // Now you can use dispatcher.Invoke anywhere you want
        dispatcher.Invoke(() =>
        {
            driver = new ChromeDriver(@"myPath");
        });

        // And async for not blocking the UI thread
        dispatcher.BeginInvoke(new Action(() =>
        {
            myFunctionUsingDriverObject(); 
        }));

        // or using await
        await dispatcher.InvokeAsync(() =>
        {

        });

        // And when you are done, you can shut the thread down
        dispatcher.InvokeShutdown();
Soroush Falahati
  • 2,196
  • 1
  • 27
  • 38
0

You could use a singleton class or if you wanted to ensure that this could only run once for all applications, a service class that is based on a Mutex. I will show you the former as this seems more applicable as far as I can make out

public interface IDriverService 
{
    void StartDriverService();
    void StopDriverService();
    void PerformDriverAction();
}

Now an implementation

public class ChromeDriverService : IDriverService 
{
    private static ChromeDriverService instance;

    private readonly Thread _thread;
    private readonly ConcurrentQueue<Action> _actions = new ConcurrentQueue<Action>();
    private volatile bool _running;

    private ChromeDriverService() 
    {
        _thread = new Thread();
        _thread.Start();            
    }

    public static IDriverService Instance()
    {
        if (instance == null)
            instance = new ChromeDriverService();
        return instance;
    }

    // This will run on the "target" _thread
    public void StartDriverService()
    {
        while (true)
        {
            Action action;
            if (_actions.TryDequeue(out action))
            {
                try
                {
                    action();
                }
                catch (Exception ex) { // Handle }
            }
            else
            {
                if (!_running && _actions.IsEmpty)
                    return;
            }
        }
    }

    public void StopDriverService()
    {
        _running = false;

        // IMPORTANT: Finish the logic here - we have a chance for race conditions. Dequeuing before the 
        // last action runs for example. This is primative, but you will have to take care of this. 
        while (!_actions.IsEmpty)
        {
            // Do stuff.
        }
    }

    // Called from some other thread.
    public void PerformDriverAction(Action action)
    {
        if (_running)
            _actions.Enqueue(action);
    }
}

This is a primitive example and I have not attempted to run or compile this (I am on my phone).

Note, I am not doing anything with the actual ChromeDriver. This class can be simply edited to work with such an object.

I hope this helps.

MoonKnight
  • 23,214
  • 40
  • 145
  • 277