Search code examples
c#androidvisual-studioxamarinmono

Wait for RunOnUIThread to finish and continue executing the rest of the task


im developing an app for android via c#(xamarin.visual studio) , the problem is that i have some task to do that running in other threads , and when it should update the layout it should call Activity.RunOnUIThread , everything it's working well but the thread dont wait this method to finnish and continue executing the rest withuout waiting.

The question is : How to wait for RunOnUIThread to finish and after that continue executing rest of the commands of the task. ?

public void start(int threadCounter)
    {
        for (int i = 0; i < threadCounter; i++)
        {

            Thread thread1 = new Thread(new ThreadStart(RunScanTcp));
            thread1.Start();

        }

    }
    public void RunScanTcp()
    {

        int port;

        //while there are more ports to scan 
        while ((port = portList.NextPort()) != -1)
        {
            count = port;

            Thread.Sleep(1000); //lets be a good citizen to the cpu

            Console.WriteLine("Current Port Count : " + count.ToString());

            try
            {

                Connect(host, port, tcpTimeout);

            }
            catch
            {
                continue;
            }

            Activity.RunOnUiThread(() =>
            {
                mdata.Add(new data() { titulli = "Port : " + port, sekuenca = "Sequence : ", ttl = "Connection Sucessfull !", madhesia = "", koha = "Time : " });
                mAdapter.NotifyItemInserted(mdata.Count() - 1);
                if (ndaluar == false)
                {
                    mRecyclerView.ScrollToPosition(mdata.Count() - 1);
                }
            }); // in that point i want to wait this to finish and than continue below...
            Console.WriteLine("TCP Port {0} is open ", port);

        }

Solution

  • First of all you should avoid creating new Threads. In you case you must use ThreadPool.QueueUserWorkItem to enqueue the CPU bound operation. Then you could use a ManualResetEventSlim or TaskCompletionSource to synchronize the the UI thread and the Worker Thread.

    Example:

    // mre is used to block and release threads manually. It is
    // created in the unsignaled state.
    
    ManualResetEventSlim mre = new ManualResetEventSlim(false);
    
    RunOnUiThread(() =>
    {            
        // Update UI here.
        // Release Manual reset event.
    
        mre.Set();
    });
    
    // Wait until UI operations end.
    mre.Wait();
    

    In your specific case:

    for (int i = 0; i < threadCounter; i++)
    {
        ThreadPool.QueueUserWorkItem(RunScanTcp);
    }
    
    private void RunScanTcp(object stateInfo) 
    {
        // Do CPU bound operation here.
        var a = 100;
        while (--a != 0)
        {
            // mre is used to block and release threads manually. It is
            // created in the unsignaled state.
            ManualResetEventSlim mre = new ManualResetEventSlim(false);
    
            Activity.RunOnUiThread(() =>
            {
                // Update UI here.
    
                // Release Manual reset event.
                mre.Set();
            });
    
            // Wait until UI operation ends.
            mre.WaitOne();
        }
    }
    

    If you prefer to use TaskCompletionSource you could use an alternative approach:

    private async void RunScanTcp(object stateInfo)
    {
        // Do CPU bound operation here.
        var a = 100;
        while (--a != 0)
        {
            // using TaskCompletionSource
            var tcs = new TaskCompletionSource<bool>();
    
            RunOnUiThread(() =>
            {
                // Update UI here.
    
                // Set result
                tcs.TrySetResult(true);
            });
    
            // Wait until UI operationds.
            tcs.Task.Wait();
        }
    }