暂停一个线程,直到一个方法和所有内部线程完成他们的工作?

时间:2014-06-10 10:59:18

标签: c# multithreading nfa automaton

我是线程的新手,我想知道如何使用它们在非确定性有限自动机中进行评估。

我有调用另一种方法的方法:

public bool Evaluate2(string s)
{
    accepted = false;

    ThreadEval(s, StartState);            

    return accepted; 
}

变量accepted是一个类成员,我用它来控制其他线程何时应该停止。

void ThreadEval(string s, State q)
{            
    if (s.Length == 0 && q.IsFinal)
    {
        accepted = true;
        return;
    }

    bool found = true;            
    State current = q;

    for (int i = 0; found && !accepted && i < s.Length; i++)
    {
        found = false;
        foreach (Transition t in current.transitions)
            if (t.symbol == s[i])
            {
                Thread thread = new Thread(new ThreadStart(delegate { ThreadEval(s.Substring(i+1), t.to); }));
                thread.Start();
                found = true;
            }
     }
}

我的每个州都有一组过渡。转换由符号和消耗该符号的状态组成。因此,无论何时找到可能的转换,我都想创建一个新线程并检查字符串的其余部分(没有当前字符)......

我目前遇到两个问题:

  • 在ThreadEval内创建的所有线程完成之前,正在执行“return accepted”。有没有办法确保它不会返回,直到那些线程结束? 我在返回之前放了一个Thread.Sleep(200)并且它工作但是对于大字符串来说200 ms可能还不够,我也不想提高值,所以小字符串需要的时间比应该处理的要长

  • 代码的方式是导致一些索引异常......我99.999%确定它的方式是正确的但是只有当我调用Substring传递值时它才会停止崩溃b>我而不是 i + 1 ...但如果我只用 i 打电话,那么它永远不会到达字符串的末尾,具体取决于自动机配置可能导致无限循环。我不确切地知道线程是如何工作的,但我怀疑在子串切片之前可能有一些并行处理正在改变 i 的值。 我怎么能保证每当我调用一个新线程时我只丢弃当前的字符?

如果有人对如何使用更优雅的线程有任何建议,我将不胜感激,到目前为止,我发现在线程分配的函数中传递参数的唯一方法是使用委托。

3 个答案:

答案 0 :(得分:6)

为了阻止线程t执行完成,您可以使用Thread.Join

t.Join();

这将主线程置于IDLE状态,直到线程t完成。 这意味着您必须跟踪foreach循环内创建的所有线程,然后逐个加入它们。

更好的方法是使用TPL&#39; Task<T>而不是直接使用线程。你的代码有点像这样:

Task ThreadEval(string s, State q)
{
    //...

    List<Task> tasks = new List<Task>();

    for (int i = 0; found && !accepted && i < s.Length; i++)
    {
        found = false;
        foreach (Transition t in current.transitions)
            if (t.symbol == s[i])
            {
                tasks.Add(
                    Task.Run(
                        () => await ThreadEval(s.Substring(i+1), t.to)));
                found = true;
            }
     }

    return Task.WhenAll(tasks);
}

await ThreadEval(...);
  1. 更改签名以返回Task而不是
  2. 创建所有正在运行的任务的列表
  3. Task.WhenAll将创建一个新任务,当tasks列表中的所有任务标记为已完成时,该任务将标记为已完成。返回任务。
  4. 然后调用者将await ThreadEval。

答案 1 :(得分:4)

写了一个小例子来帮助你理解如何做到这一点https://dotnetfiddle.net/2Djdh7

基本上,您可以使用Join()方法等待线程完成,并且通过在线程列表上使用ForEach()方法,您可以等待所有线程在一行中完成。

public class Program
{
    public static void Main(string[] args)
    {
        List<int> list = new List<int>();
        list.AddRange(new int[] {10, 200, 300, 400, 234 });

        // create a bunch of threads
        List<Thread> threads = new List<Thread>();
        list.ForEach(x => threads.Add(new Thread(() => ThreadMethod(x))));

        // start them
        threads.ForEach(x => x.Start());

        // wait for them to finish
        threads.ForEach(x => x.Join());

        // this will not print untill all threads have completed
        Console.WriteLine("Done");
    }

    private static void ThreadMethod(int i)
    {
        Thread.Sleep(i);
        Console.WriteLine("Thread: " + i);
    }
}

输出:

Thread: 10
Thread: 200
Thread: 234
Thread: 300
Thread: 400
Done

答案 2 :(得分:1)

我认为async/await模式会帮助你。然后你可以做类似的事情:

List<Task> runningEvals = new List<Task>();

async Task ThreadEval(string s, State q)
{            
    if (s.Length == 0 && q.IsFinal)
    {
        Task.WaitAll(runningEvals.ToArray()); // wait for all tasks to finish
        runningEvals.Clear();

        accepted = true;
        return;
    }

    bool found = true;            
    State current = q;

    for (int i = 0; found && !accepted && i < s.Length; i++)
    {
        found = false;
        foreach (Transition t in current.transitions)
            if (t.symbol == s[i])
            {
                // start a task and add it to the "running tasks" list
                var task = Task.Run(async () => await ThreadEval(s.Substring(i+1), t.to));
                runningEvals.Add(task);
                found = true;
            }
     }
}

注意:这不是经过测试的代码(由于“共享List<Task>”,它绝对不是“线程保存”,但只应指向一个方向。

这可以通过以下方式称为“asynchonous”:

await ThreadEval(s, StartState);

或者如果你不能一直“异步”(并且只有那时):

ThreadEval(s, StartState).Wait();