【问题标题】:Starting two threads runs them in sequence and not at the same time启动两个线程按顺序而不是同时运行它们
【发布时间】:2013-04-17 11:52:37
【问题描述】:

我有一个控制窗体的主线程,在按下按钮时会执行两个线程。一个用于记录信息,另一个用于读取信息。将这些放入线程背后的想法是让用户能够在执行时与界面进行交互。

这里是两个线程的创建;

Thread recordThread = new Thread(() => RecordData(data));
recordThread.Name = "record";
recordThread.Start();

Thread readThread = new Thread(() => ReadData(data));
readThread.Name = "read";
readThread.Start();

data 只是一个数据对象,用于存储在录制过程中记录的数据。

我面临的问题是第一个线程执行得很好,第二个拒绝运行,直到第一个完成。在第二个线程函数中放置一个断点,ReadData 让我知道只有在第一个线程完成所有记录后才会调用它。

我已经尝试解决这个问题几个小时了,但我无法理解它为什么会这样做。添加一个;

while(readThread.IsAlive) { }

在开始之后将停止执行之后的任何内容,并且它的状态是正在运行。但它不会转到给定的方法。

有什么想法吗?

编辑: 线程调用的两个函数是;

    private void RecordData(Data d)
    {
        int i = 0;
        while (i < time * freq)
        {
            double[] data = daq.Read();
            d.AddData(data);
            i++;
        }
    }

    private void ReadData(Data d)
    {
        UpdateLabelDelegate updateData =
            new UpdateLabelDelegate(UpdateLabel);

        int i = 0;
        while (i < time * freq)
        {
            double[] data = d.ReadLastData();
            this.Invoke(updateData, new object[] { data });
            i++;
        }
    }

data 对象在调用的两个函数中都有锁定; ReadLastData 和读取

这里是 Data 对象中的方法。

    public void AddData(double[] data)
    {
        lock (this)
        {
            int i = 0;
            foreach (double d in data)
            {
                movementData[i].Add(d);
                i++;
            }
        }
    }

    public double[] ReadLastData()
    {
        double[] data = new double[channels];
        lock (this)
        {
            int i = 0;
            foreach (List<double> list in movementData)
            {
                data[i] = list[list.Count - 1];
            }
        }
        return data;
    }

【问题讨论】:

  • 这取决于第二个线程是否正在等待第一个线程锁定的资源(猜测data)。你有什么锁?
  • 请展示一个简短但完整的程序来说明问题。
  • 请显示更多代码,这还不够
  • @Belogix AFAIK 锁定不会阻止线程本身运行。然而,它会阻塞 inside 该线程,直到另一个线程释放该对象。
  • 这就是我的意思@James,我们不知道他在线程内做什么(即一个会锁定另一个)。我并不是说它会阻止它继续前进。

标签: c#


【解决方案1】:

看起来您的阅读/写作之间存在竞争条件。在您的第一个线程中,您在向对象添加数据的同时锁定对象,而在第二个线程中,您尝试 获得对它的排他锁以开始读取。但是,问题是第一个线程执行得如此之快,以至于第二个线程永远没有机会获得锁。

这个问题的解决方案真的取决于你在这里追求什么样的行为。如果您希望在每次写入后获得连续读取,那么您需要做的是控制读取/写入操作之间的执行,例如

static AutoResetEvent canWrite = new AutoResetEvent(true); // default to true so the first write happens
static AutoResetEvent canRead = new AutoResetEvent(false);
...
private void RecordData(Data d)
{
    int i = 0;
    while (i < time * freq)
    {
        double[] data = daq.Read();
        canWrite.WaitOne(); // wait for the second thread to finish reading
        d.AddData(data);
        canRead.Set(); // let the second thread know we have finished writing
        i++;
    }
}

private void ReadData(Data d)
{
    UpdateLabelDelegate updateData =
        new UpdateLabelDelegate(UpdateLabel);

    int i = 0;
    while (i < time * freq)
    {
        canRead.WaitOne(); // wait for the first thread to finish writing
        double[] data = d.ReadLastData();
        canWrite.Set(); // let the first thread know we have finished reading
        this.Invoke(updateData, new object[] { data });
        i++;
    }
}

【讨论】:

  • 这似乎解决了函数触发的问题!好的!唯一的问题是,这会导致我的系统无法正常运行,因为它现在太慢了。我将开始测试它读取每十分之一左右的值。但这是另一个问题,这是这个问题的解决方案。谢谢!
  • @Sander 你到底需要做什么?您真的需要在写入期间进行读取吗?仅供参考您对 lock statement 的使用 - 不建议自己锁定类型,即 lock(this) 违反了准则。相反,您应该在 data 实例之外有一个锁定对象(类似于我在示例中使用 AutoResetEvent 的方式)。您实际上可能会发现这就是潜在的问题所在。
  • 已将代码更改为您推荐的代码,正如我之前所说,它确实有效,只是它太慢了。我想做的是从数据采集卡中实时获取数据并将其呈现给用户。这就是为什么我希望在写入期间读取数据。我从记录中获得的信息比通读更重要,所以我必须弄清楚我应该如何优先考虑记录而不是阅读。
  • @Sander - 如果您恢复到原始代码,但不是在内部锁定 data,而是在您的 RecordData/ReadData 方法中将其从外部锁定,该怎么办?如果您需要在数据进入时打印数据,您不能只处理写入内部的UpdateLabelDelegate 吗?如此有效地您在同一操作中进行读/写。
  • 我之前确实尝试过,但是获取速度太慢了。回去并再次查看它似乎问题不是线程运行太慢,而是我一次从硬件获得一个值。将 THAT 更改为 50 值会显着提高性能。感谢您的帮助!
【解决方案2】:

您可以尝试在 RecordData 中添加睡眠吗? 也许只是您的(单核 CPU ??)Windows 操作系统不允许第二个线程获得 CPU 资源。

不要这样做: 锁(这个)

改为这样做:

private object oLock = new object();

[...]

lock (this.oLock)

编辑: 你能试试这样的电话吗:

Thread recordThread = new Thread((o) => RecordData((Data)o));
recordThread.Name = "record";
recordThread.Start(data);

【讨论】:

  • 我正在运行一个有 4 个内核的系统。据我了解,多个线程甚至应该能够在单个内核上运行。但无论如何,就我而言,这真的不是是个问题。 (编辑:忘记了“不”)
  • oLock 更改的事件? : /
  • 是的。问题似乎与线程有关,而不是与数据对象有关。如果我在它运行 ReadData 方法之前设置断点,则在第一个线程完成运行之前不会触发它。
  • @Sander 问题与数据对象有关,而不是线程。您在执行添加时将其锁定,而不是让第二个线程有机会锁定它以执行读取。
  • @James 我现在注意到第二个线程(读取)的所有更新都在第一个线程完成后完成,即 UI 更新都在第一个线程完成后完成。这可能与调用而不是线程有关。我将不得不继续阅读有关调用/委托的内容,看看这是否是骗子。
猜你喜欢
  • 2013-08-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-09
  • 1970-01-01
相关资源
最近更新 更多