c#双线程,线程.IsAlive是false,即使这个线程还没有完成

本文关键字:线程 还没有 false IsAlive | 更新日期: 2023-09-27 18:07:33

我写了一个简短的程序,它搜索空目录并删除它们。这个进程应该在后台运行,而另一个进程应该每秒向控制台写入一些内容,以便用户知道程序仍在运行。我的问题是整个程序在大约3秒后停止,而processDirectory方法甚至没有完成。

My Main Method调用在第二个线程中运行的方法(processDirectory()):

static void Main(string[] args)
{
    Thread delEmpty = new Thread(() => Thread2.processDirectory(@"C:'Users'Mani'Documents"));
    delEmpty.Start();
    printRunning(delEmpty);
    File.WriteAllLines(@"C:'Users'Mani'Desktop'Unauthorized Folders.txt", Thread2.unauthorized);
    File.WriteAllLines(@"C:'Users'Mani'Desktop'Empty Folders.txt", Thread2.emptyFolders);
    Console.ReadKey();
}

我的第二个类,存储我的processDirectory方法,应该在后台运行:

public static List<string> unauthorized = new List<string>();
public static List<string> emptyFolders = new List<string>();
public static void processDirectory(string rootPath)
{
    if (!Directory.Exists(rootPath)) return;
    foreach (var dir in Directory.GetDirectories(rootPath))
    {
        try
        {
            processDirectory(dir);
            if (Directory.GetFiles(dir).Length == 0 && Directory.GetDirectories(dir).Length == 0) Directory.Delete(dir, false);
        }
        catch (UnauthorizedAccessException uae) { unauthorized.Add(uae.Message); }
    }
}

打印代码:

static async void printRunning(Thread delEmpty)
{
    Console.CursorVisible = false;
    for (int cnt = 1; delEmpty.IsAlive; cnt++)
    {
        switch (cnt)
        {
            case 1:
                Console.Write("Running.  ");
                break;
            case 2:
                Console.Write("Running . ");
                break;
            case 3:
                Console.Write("Running  .");
                cnt = 0;
                break;
        }
        await Task.Delay(1000);
    }
    Console.Write("Finished!");
    Console.CursorVisible = true;
}

c#双线程,线程.IsAlive是false,即使这个线程还没有完成

我建议您避免使用线程,而使用一个抽象来为您处理线程问题。我建议使用微软的响应式框架团队的响应式扩展(NuGet "System.Reactive")和交互式扩展(NuGet "System.Interactive")。

然后你可以这样做:

static void Main(string[] args)
{
    var rootPath = @"C:'Users'Mani'Documents";
    using (Observable
        .Interval(TimeSpan.FromSeconds(1.0))
        .Subscribe(x => Console.WriteLine($"Running{"".PadLeft((int)x % 3)}.")))
    {
        Thread2.processDirectory(rootPath);
    }
}
public static class Thread2
{
    public static List<string> unauthorized = new List<string>();
    public static List<string> emptyFolders = null;
    public static void processDirectory(string rootPath)
    {
        if (!Directory.Exists(rootPath)) return;
        emptyFolders = 
            EnumerableEx
                .Expand(Directory.GetDirectories(rootPath), dir => Directory.GetDirectories(dir))
                .Where(dir => Directory.GetFiles(dir).Length == 0 && Directory.GetDirectories(dir).Length == 0)
                .ToList();
        emptyFolders
            .AsEnumerable()
            .Reverse()
            .ForEach(dir =>
            {
                try
                {
                    Directory.Delete(dir, false);
                }
                catch (UnauthorizedAccessException uae) { unauthorized.Add(uae.Message); }
            });
    }
}

这里的关键元素是:

  1. Observable.Interval设置定时器,每秒显示"Running"消息。
  2. EnumerableEx.Expand,它递归地构建要删除的文件夹列表。
  3. 运行要删除的文件夹(反向)并删除它们的Reverse/ForEach
重要的是不要让删除发生在主线程上——它只是在另一个线程上出现的"Running"消息。如果需要的话,将删除推到另一个线程是相当容易的,但这不是必需的。

处理GetDirectories抛出错误的情况,使用以下代码:

    Func<string, string[]> getDirectories = dir =>
    {
        try
        {
            return Directory.GetDirectories(dir);
        }
        catch (UnauthorizedAccessException uae)
        {
            unauthorized.Add(uae.Message);
            return new string[] { };
        }
    };
    emptyFolders =
        EnumerableEx
            .Expand(getDirectories(rootPath), dir => getDirectories(dir))
            .Where(dir => Directory.GetFiles(dir).Length == 0 && getDirectories(dir).Length == 0)
            .ToList();

有两种解决方法:

  1. 让printRunning方法同步运行
  2. 添加delEmpty.Join()以便主线程在delEmpty线程完成时等待

    delEmpty.Start();
    printRunning(delEmpty);
    delEmpty.Join();
    

在第一个解决方案的情况下,将printRunning方法替换为以下方法

static void printRunning(Thread delEmpty)
{
    Console.CursorVisible = false;
    for (int cnt = 0; delEmpty.IsAlive; cnt++)
    {
        switch (cnt % 3)
        {
            case 0:
                Console.Write("Running.");
                break;
            case 1:
                Console.Write("Running..");
                break;
            case 2:
                Console.Write("Running...");
                break;
        }
        Thread.Sleep(1000);
        Console.SetCursorPosition(0, 0);
        Console.Clear();
    }
    Console.Write("Finished!");
    Console.CursorVisible = true;
}
相关文章: