6

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

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

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;
            }
     }
}

我的每个状态都有一组转换。转换由符号和通过使用该符号可以进入的状态组成。因此,每当找到可能的转换时,我都想创建一个新线程并检查字符串的其余部分(没有当前字符)...

我目前有2个问题:

  • 在 ThreadEval 中创建的所有线程完成它们之前,正在执行“接受的返回”。有没有办法确保在这些线程完成之前它不会返回?我在返回之前放了一个 Thread.Sleep(200) 并且它有效,但是对于大字符串来说 200 毫秒可能还不够,我也不想提高这个值,所以小字符串需要比它们应该处理的时间更长的时间.

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

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

4

3 回答 3

6

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

t.Join();

这会将主线程置于空闲状态,直到线程t完成。这意味着您必须跟踪foreach循环内创建的所有线程,然后将它们一一连接。

更好的方法是使用 TPLTask<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. 更改签名以返回 aTask而不是 void
  2. 创建所有正在运行的任务的列表
  3. Task.WhenAll将创建一个新任务,当列表中的所有任务tasks本身都标记为完成时,该任务将被标记为完成。返回任务。

然后调用者将awaitThreadEval。

于 2014-06-10T11:06:35.437 回答
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
于 2014-06-10T11:19:26.823 回答
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>),但应该只为您指明一个方向。

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

await ThreadEval(s, StartState);

或者如果你不能“一直异步”(只有这样):

ThreadEval(s, StartState).Wait();
于 2014-06-10T11:09:38.937 回答