Abort Thread through another C#process

Asked

Viewed 828 times

5

I am developing an application in ASP.Net 5.0, where it needs to start a task through a Thread Secondary, as the example below:

//Inicia processo de pesquisa            
var ppBlo = new ProcessoPesquisaBLO();

//Cria a Thread para rodar o processo
Thread tProcesso = new Thread(ppBlo.IniciarPesquisa);
tProcesso.Priority = ThreadPriority.Highest;
tProcesso.Start();

This implementation is within a function that can be triggered by different users at different times, because it is a web application.

However, in a second screen has the listing of all Threads. And the user may interrupt the execution at any time.

Besides having a limit of Threads running concurrently that the user may have.

Doubt

Precious in some way to abort the Threads which are running (since the user may close at any time). And the function that will do this procedure, does not have access to the attributes of the class of the function from which the Threads.

I don’t know if it’s useful for anything, but I’m storing the attribute value ManagedThreadId of each Thread created in the database.

  • Kind of dangerous, but if you do, you can create a Singleton class that has a thread list with user identification, then you can access from any part of the system, could filter by user finish and etc. I would use Task, is more current, and better adapts to various situations, besides having the Continuewith method, with it you can put an action for when the task ends, as remove it from the list for example.

2 answers

3


In . Net threads are automatically recycled when they finish their work. The only thing Voce has to ensure is that the threads end as quickly as possible. Also there is no way easy to retrieve all Managed threads associated with a process, so you should store the threads you create in a list.

private static readonly List<Thread> _threads = new List<Thread>();

Thread tProcesso = new Thread(ppBlo.IniciarPesquisa);
tProcesso.Priority = ThreadPriority.Highest;
tProcesso.Start();
_threads.Add(tProcesso);

Now Voce can Interrupt threads

The idea is that you interrupt the thread so that it comes out with ThreadInterruptedException.

For that Voce has to call the method Interrupt thread.

//procure as threads do usuario aqui
var threads = _threads.where(t => t.ManagedThreadId == 1);
foreach(var thread in threads){
    thread.Interrupt();
    _threads.Remove(thread); //lembre-se de apagar a thread da lista
}

Note that if threads never enter a blocking condition like Thread.Sleep, WaitForSingleObject (like the Mutex.WaitOne), Thread.Join, this exception will never run (this only happens if the thread is 100% processor work).


As suggested in the comments by @Fbatista the best was to have used a mechanism that allowed cancellation as the task.

I explained in this question how cancellation can be done

2

Well, the first necessary thing you’re already doing, which is to save the Managedthreadid.

For the user to abort, you also need to save the list to show only his, but that’s not what the question is about.

The first step is to import the DLL kernel32.dll and use to open and end Thread by Managedthreadid. Would look like this:

    [System.Runtime.InteropServices.DllImport("kernel32.dll")]
    static extern IntPtr OpenThread(uint dwDesiredAccess, bool bInheritHandle, uint dwThreadId);


    [System.Runtime.InteropServices.DllImport("kernel32.dll")]
    static extern bool TerminateThread(IntPtr hThread, uint dwExitCode);

After that, just finish the selected Thread, this way:

        IntPtr ptrThread = OpenThread(1, false, (uint)id);
        TerminateThread(ptrThread, 1);

See a full example in the code below:

class Program
{

    [System.Runtime.InteropServices.DllImport("kernel32.dll")]
    static extern IntPtr OpenThread(uint dwDesiredAccess, bool bInheritHandle, uint dwThreadId);


    [System.Runtime.InteropServices.DllImport("kernel32.dll")]
    static extern bool TerminateThread(IntPtr hThread, uint dwExitCode);


    static void Main(string[] args)
    {
        Console.Out.WriteLine("Forking off threads...");
        for (int i = 0; i < 5; i++)
        {
            Thread t = new Thread(new ThreadStart(RunInfinite));
            Console.Out.WriteLine("Thread " + t.Name + "(ManagedThreadId: " + t.ManagedThreadId + ") created!");
        }


        ProcessThreadCollection processThreads = Process.GetCurrentProcess().Threads;
        Console.Out.WriteLine("=> Total threads: " + processThreads.Count);
        foreach (ProcessThread pt in processThreads)
        {
            int timerSeconds = 5;
            while (timerSeconds-- > 0)
            {
                Console.Out.Write("\r Seconds before thread " + pt.Id + " dies: " + timerSeconds);
                System.Threading.Thread.Sleep(1000);
            }


            IntPtr ptrThread = OpenThread(1, false, (uint)pt.Id);
            if (AppDomain.GetCurrentThreadId() != pt.Id)
            {
                try
                {
                    TerminateThread(ptrThread, 1);
                    Console.Out.Write(". Thread killed.\n");
                }
                catch (Exception e)
                {
                    Console.Out.WriteLine(e.ToString());
                }
            }
            else
                Console.Out.Write(". Not killing... It's the current thread!\n");


        }
        Console.Out.WriteLine("=> Total threads now: " + Process.GetCurrentProcess().Threads.Count);
        Console.ReadLine();
    }




    public static void RunInfinite()
    {
        while (true)
        {
            System.Threading.Thread.Sleep(10000);
        }
    }
}

Another way would be the same this answer, where you would search for threads and check if the id is the same, but this way I wouldn’t advise much, because from what I said, will have many threads in your system.

public void KillThread(int index)
    {
        string id = string.Format("MyThread{0}",index);
        foreach (Thread thread in _threads)
        {
            if (thread.Name == id)
                thread.Abort();
        }
    }

References:

  • I voted against because I believe that this is not an appropriate solution to the problem.

  • @Brunocosta Could you inform the reason you think so? Not criticizing your thinking, just so I see a way to improve the answer.

  • Yes. It has to do with the fact that the threads on . net are automatically managed. That’s when they finish their work. net finishes them (or reuses them). What Voce is doing in your code is creating native threads and I believe this is not the best solution.

  • @Brunocosta So I just made an example for him to understand. From what the AO said, it has threads per user, and that should take time to complete. It has a table in the bank with the Managedthreadid of each Thread. Basically, it should change the field id of the code I posted, by the bank ID he claims to have on the question.

  • I find this approach a little strange, but without knowing the AP scenario, there is no way I can say whether it is "right or wrong". What you can do is show how to delete a Thread by Managedthreadid, which is what was asked. But I understand your point of view.

  • @Randrade will try to explain the scenario. It is a tool that will be used by Call Center. And daily Anatel sends a list of Cpfs wanting to get all the recordings of the calls made for the same. Depending on the file size, it may take hours. But at any time the user may want to stop this process (because it imported the wrong file, and so on). And you can’t have a lot of searches running simultaneously per user, because it might burden the network and the servers. Maybe then you’ll understand better.

  • @Diegomoreno Have you thought about task as suggested in the other answer?. However, the answer does not work in the model quoted in the question?

Show 2 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.