C#如何在启动下一个线程之前检查线程是否已完成

3

这是我第一次尝试在应用程序中使用线程。虽然我知道这个问题以前已经被问过了,但是在我查看的解决方案中,我无法看出如何将它们应用到我的情况中。

我有一个程序,其中一个datagridview每60秒刷新一次。数据来自SQL数据库。这个计时器也会启动一个工作线程,在后台查找特定的蓝牙设备并根据其结果更新数据库。由于蓝牙查找特别慢,因此我将其放在了工作线程中。

我的问题是,有时新的工作线程会在之前的线程完成之前启动。至少这是我得到的错误的唯一合理解释。最明显的提示是文件锁定错误,当唯一可能锁定文件的是同一应用程序的另一个工作线程时。

以下是我用来启动后台线程的代码。

private void timerScreenRefresh_Tick(object sender, EventArgs e)
{
    if (LocalUtilities.Debug > 3) LocalUtilities.writeLogFile(4, "Primary", LocalUtilities.getCurrentMethod() + "()", "");

    // If the user is not on a Remote desktop connection
    if (!remoteDesktopUser)
    {

        // Run the Bluetooth Search in a worker thread
        Thread thread = new Thread(new ThreadStart(this.checkProximity));
        thread.IsBackground = true;
        thread.Start();
    }

    // Load User Data from the DB and display on the screen
    loadUserData();
}

似乎解决方案是使用thread.IsAlive(),但我找不到一个好的例子。在刚刚用"Thread thread = new Thread()"创建了一个新线程时尝试检查线程的存在似乎很奇怪。
显然,我漏掉了什么。我正在使用Visual Studio 2008。 感谢任何想法 David
更新
基于krw12572提出的解决方案,我尝试了这个...
我将!=改为==,因为我仍然希望每次都在主线程中运行loadUserData()方法。
在编辑器中,我会在"_bluetoothSearchThread"上得到绿色下划线,告诉我该字段从未被分配,并且永远具有NULL值。
在运行时,我在这一行收到了"Object Reference Not Set to An instance of an object"错误。
if (_bluetoothSearchThread == null && _bluetoothSearchThread.IsAlive)

这个值是如何被分配的?
    private Thread _bluetoothSearchThread;
    private void timerScreenRefresh_Tick(object sender, EventArgs e)
    {
        if (LocalUtilities.Debug > 3) LocalUtilities.writeLogFile(4, "Primary", LocalUtilities.getCurrentMethod() + "()", "");

        // Check if Worker Thread is already running.
        if (_bluetoothSearchThread == null && _bluetoothSearchThread.IsAlive)
        {
            if (LocalUtilities.Debug > 3) LocalUtilities.writeLogFile(4, "Primary", LocalUtilities.getCurrentMethod() + "()", "Previous Worker Thread not running");

            // If the user is not on a Remote desktop connection
            if (!remoteDesktopUser)
            {
                // Check if the users mobile phone is within range
                // Run the Bluetooth Search in a worker thread

                Thread thread = new Thread(new ThreadStart(this.checkProximity));
                thread.IsBackground = true;
                thread.Start();
            }
        }
        else 
        {
            if (LocalUtilities.Debug > 3) LocalUtilities.writeLogFile(4, "Primary", LocalUtilities.getCurrentMethod() + "()", "Worker Thread still running don't start another one");
        }

        // Load User Data from the DB and display on the screen
        loadUserData();
    }

更新2

好的,我想我已经弄清楚了。 我把“!= Null”改回了原样,并把IF和Else的顺序倒过来。

然后我得动一下脑筋,将“thread”改成了“_bluetoothSearchThread”。

现在代码可以编译并运行。现在我只需要通过触发导致文件锁定错误的条件来测试它,看看是否真正解决了原始问题。如果有效,我将标记krw12572的答案为正确答案。

更新2.5 我还不得不移动这一行,以便它不会太早地创建新实例。

_bluetoothSearchThread = new Thread(new ThreadStart(this.checkProximity));

所以这就是可行的解决方案。
    private Thread _bluetoothSearchThread;
    private void timerScreenRefresh_Tick(object sender, EventArgs e)
    {
        if (LocalUtilities.Debug > 3) LocalUtilities.writeLogFile(4, "Primary", LocalUtilities.getCurrentMethod() + "()", "");


        // Check if Worker Thread is already running.
        if (_bluetoothSearchThread != null && _bluetoothSearchThread.IsAlive)
        {
            // Thread is still running.  Just log it and move on.
            if (LocalUtilities.Debug > 3) LocalUtilities.writeLogFile(4, "Primary", LocalUtilities.getCurrentMethod() + "()", "******** Worker Thread still running don't start another one *********");
        }
        else 
        {
            if (LocalUtilities.Debug > 3) LocalUtilities.writeLogFile(4, "Primary", LocalUtilities.getCurrentMethod() + "()", "Previous Worker Thread not running");

            // If the user is not on a Remote desktop connection
            if (!remoteDesktopUser)
            {
                // Check if the users mobile phone is within range
                // Run the Bluetooth Search in a worker thread
                _bluetoothSearchThread = new Thread(new ThreadStart(this.checkProximity));
                _bluetoothSearchThread.IsBackground = true;
                _bluetoothSearchThread.Start();
            }
        }

        // Load User Data from the DB and display on the screen
        loadUserData();
    }

这里是您的结果:https://dev59.com/v2nWa4cB1Zd3GeqP3r5N - BALA s
@BALA 我之前看过那个解决方案。它似乎过于复杂,我并没有真正理解它。明天我会尝试盲目地跟随它,看看是否能得出可行的结果。 - David P
可能是重复的问题:如何检查线程是否执行完毕 - Ashwin Gupta
@AshwinGupta 我已经阅读了那篇文章,它提出了几个不同的选项,但并没有解释如何实际使用它们。至少我无法理解。 - David P
3个回答

4

编辑:

经过研究,在您的情况下使用Thread.IsAlive不是一种安全的方法。

您应该使用Threa.Join()

文档:

阻止调用线程,直到由此实例表示的线程终止或指定的时间过去,同时继续执行标准的COM和SendMessage泵送。

示例:

while(!currentThread.Join(0)) //should specify the time if you dont want it to be blocking.
{
    ///thread is ongoing
}
Console.WriteLine("while loop has breaked! so the thread is finished!");

在我的示例代码中,我该如何使用Thread.IsAlive?你能给我一些想法吗? - David P
@LorenceHernandez 我不想在前一个工作线程仍在运行时阻塞主线程。只需不启动另一个工作线程并在主线程中继续处理即可。主线程将在下一个计时器滴答声中再次检查工作线程的存在。 - David P

4
如果你希望同时只有一个线程运行,那么你可以创建一个字段来存储线程实例。使用该线程实例,你可以通过使用`_threadInstance.IsAlive`来检查它是否已经在运行。
private Thread _bluetoothSearchThread;
private void timerScreenRefresh_Tick(object sender, EventArgs e)
{
    if(_bluetoothSearchThread != null && _bluetoothSearchThread.IsAlive) 
        return;        //It means one thread is already performing the search operation.

    if (LocalUtilities.Debug > 3) LocalUtilities.writeLogFile(4, "Primary", LocalUtilities.getCurrentMethod() + "()", "");

    // If the user is not on a Remote desktop connection
    if (!remoteDesktopUser)
    {

        // Run the Bluetooth Search in a worker thread
        _bluetoothSearchThread = new Thread(new ThreadStart(this.checkProximity));
        _bluetoothSearchThread.IsBackground = true;
        _bluetoothSearchThread.Start();
    }

    // Load User Data from the DB and display on the screen
    loadUserData();
}

我尝试过了。它有一些问题。我更新了我的问题,包括您提出的解决方案和错误信息。谢谢David。 - David P
我更新了我的回答。不要创建Thread thread = new Thread(..),而是使用新创建的字段来作为线程实例。_bluetoothSearchThread = new Thread(...) - Kamalesh Wankhede

0

你可能想要使用 Task 而不是 Thread

// Create dummy task
Task task = Task.Run(() => { });

private void timerScreenRefresh_Tick(object sender, EventArgs e)
{
    ...
    // add continuation to current task
    task.ContinueWith(t => checkProximity);
    ...
}

新任务只有在前一个任务执行完毕后才会被执行。

但是,如果任务在“Tick”期间没有时间运行,则它们将在队列中累积。


嗨,亚历山大,我在考虑把任务作为替代方案,虽然我没有排队子任务的必要。现在我明白了krw12572所建议的,并且我上面的解决方案现在也可以工作了。为了测试,我将计时器的tick设置为10秒,从我的日志中可以看到它在各个地方开启和关闭线程。有了以上代码,一切都很有序。Thread 11开始后,除了Thread 11完成之外,没有其他线程开始。因此,我现在得到的任何其他错误与重叠的线程无关。感谢您的想法。David - David P

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接