我是线程的新手,我想知道如何使用它们在非确定性有限自动机中进行评估。
我有调用另一种方法的方法:
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 的值。 我怎么能保证每当我调用一个新线程时我只丢弃当前的字符?
如果有人对如何使用更优雅的线程有任何建议,我将不胜感激,到目前为止,我发现在线程分配的函数中传递参数的唯一方法是使用委托。
答案 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(...);
Task
而不是Task.WhenAll
将创建一个新任务,当tasks
列表中的所有任务标记为已完成时,该任务将标记为已完成。返回此任务。然后调用者将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();