如何使用相同的变量来防止线程

时间:2017-10-08 09:51:33

标签: c# multithreading

我有一个多行文本框,我想用多线程处理每一行。

文本框可能有很多行(1000+),但线程不多。我想使用自定义线程数来读取所有1000多行而没有任何重复(如在每个线程中只读取UNIQUE行,如果一行已被其他线程读取,则不再读取它)。

我现在拥有的:

private void button5_Click(object sender, EventArgs e)
{
    for (int i = 0; i < threadCount; i++)
    {
        new Thread(new ThreadStart(threadJob)).Start();
    }
}

private void threadJob()
{
    for (int i = 0; i < txtSearchTerms.Lines.Length; i++)
    {
        lock (threadLock)
        {
            Console.WriteLine(txtSearchTerms.Lines[i]);
        }
    }
}

它确实启动了正确数量的线程,但它们都多次读取相同的变量。

3 个答案:

答案 0 :(得分:3)

计算后分离数据收集和数据处理以及下一步可能的步骤。您可以使用ConcurrentBag<T>安全地收集并行计算的结果,这只是线程安全的集合 那你就不用担心&#34;锁定&#34;对象和所有行将被处理&#34;只有一次。
1.收集数据
2.并行执行收集的数据
3.处理计算结果

private string Process(string line)
{
    // Your logic for given line
}

private void Button_Click(object sender, EventArgs e)
{
    var results = new ConcurrentBag<string>();

    Parallel.ForEach(txtSearchTerms.Lines,
                     line =>
                     {
                         var result = Process(line);
                         results.Add(result);
                     });

    foreach (var result in results)
    {
        Console.WriteLine(result);
    }
}  

默认情况下,Parallel.ForEach将使用底层调度程序提供的线程数。

您可以通过将ParallelOptions的实例传递给Parallel.ForEach方法来控制已用线程的数量。

var options = new ParallelOptions
{
    MaxDegreeOfParallelism = Environment.ProcessorCount
};
var results = new ConcurrentBag<string>();
Parallel.ForEach(values,
                 options,
                 value =>
                 {
                     var result = Process(value);
                     results.Add(result);
                 });

答案 1 :(得分:0)

考虑使用execve()迭代fork数组。 它就像一个普通的Parallel.ForEach循环(即每个值只会处理一次),但工作是并行完成的 - 多个Lines s(线程)。

foreach

以上代码需要在您的文件顶部添加此行:

Task

或者,如果您不仅要执行某些内容,还要 return / project ,那么请尝试:

var data = txtSearchTerms.Lines;
var threadCount = 4; // or whatever you want

Parallel.ForEach(data, 
    new ParallelOptions() { MaxDegreeOfParallelism = threadCount },
    (val) =>
    {
        //Your code here
        Console.WriteLine(val);
    });

仍然并行执行代码,但也返回using System.Threading.Tasks; (在这种情况下,原始var results = data.AsParallel(new ParallelLinqOptions() { MaxDegreeOfParallelism = threadCount }).Select(val => { // Your code here, I just return the value but you could return whatever you want return val; }).ToList(); 中的值相同)。最重要的是, List与您的输入的顺序相同。

答案 2 :(得分:-1)

有很多方法可以做到你想要的。

采取额外的课程字段:

private int _counter;

使用它而不是循环索引。在锁内增加它:

private void threadJob()
{
    while (true)
    {
        lock (threadLock)
        {
            if (_counter >= txtSearchTerms.Lines.Length)
                return;
            Console.WriteLine(txtSearchTerms.Lines[_counter]);
            _counter++;
        }
    }
}

它有效,但效率很低。

让我们考虑另一种方式。每个线程将独立于其他线程处理其数据集的一部分。

public void button5_Click(object sender, EventArgs e)
{
    for (int i = 0; i < threadCount; i++)
    {
        new Thread(new ParameterizedThreadStart(threadJob)).Start(i);
    }
}

private void threadJob(object o)
{
    int threadNumber = (int)o;
    int count = txtSearchTerms.Lines.Length / threadCount;
    int start = threadNumber * count;
    int end = threadNumber != threadCount - 1 ? start + count : txtSearchTerms.Lines.Length;

    for (int i = start; i < end; i++)
    {
        Console.WriteLine(txtSearchTerms.Lines[i]);
    }
}

这更有效,因为线程不会等待锁定。但是,数组元素的处理方式不一般。