【问题标题】:A value from one thread influencing the path of another thread一个线程的值影响另一个线程的路径
【发布时间】:2016-07-22 10:56:57
【问题描述】:

我有一个使用两个客户端线程和一个服务器的程序。在我的程序中,我希望一个线程中的值影响另一个线程的路径。

更具体地说,我在服务器中有这段代码:

class Handler 
{
    public void clientInteraction(Socket connection, bool isFirstThread, Barrier barrier)

    {
        string pAnswer = string.Empty;
        string endGameTrigger = string.Empty;
        //setup streamReaders and streamWriters

        while(true) //infinite game loop
        {
            //read in a question and send to both threads.
            pAnswer = sr.ReadLine();
            Console.WriteLine(pAnswer);

            awardPoints(); 

            writeToConsole("Press ENTER to ask another question or enter 0 to end the game", isFirstThread);
            if(isFirstThread == true)
            {
                endGameTrigger = Console.ReadLine(); //this is only assigning to one thread...
            }

            barrier.SignalAndWait();

            if(endGameTrigger == "0")//...meaning this is never satisfied in one thread
            {
                endGame();
            }           

        }
    }
}   

布尔值isFirstThread 是在线程的构造函数中设置的值,我可以检测到哪个线程首先连接到该值。

是否有某种方式,或者可能是线程方法,允许第二个连接的线程检测到第一个线程中的endGameTrigger 已设置,因此两个线程都正确执行endGame() 方法。

【问题讨论】:

  • 使其成为字段而不是本地字段。您还需要添加同步。

标签: c# multithreading server


【解决方案1】:

最好关注多线程

  • 如果出于性能/UI 原因绝对有必要启动单独的线程
  • 如果您的代码可能在多线程环境(如网站)中运行,并且您需要知道当多个线程对同一类或相同值进行操作时它不会中断。

但要格外小心。多个线程的不正确使用/处理可能会导致您的代码行为不可预测且不一致。某些东西大部分时间都会起作用,然后无缘无故地不起作用。错误将难以重现和识别。

话虽如此,处理多线程的基本概念之一是确保两个线程不会尝试同时更新相同的值。它们可以以单个线程不可能的方式破坏或部分修改值。

实现此目的的一种方法是使用locking

private object _lockObject = new Object();
private string _myString;

void SetStringValue(string newValue)
{
    lock(_lockObject)
    {
        _myString = newValue;
    }
}

您通常有一个对象,它的存在只是用作锁。当一个线程进入lock 块时,它会获取对象上的锁。如果另一个线程已经对该对象锁定,则下一个线程只是等待前一个线程释放锁定。这样可以确保两个线程不能同时更新值。

您希望保持lock 中的代码量尽可能小,以便尽快释放锁。并且请注意,如果它因多个锁而变得复杂,那么两个线程可能会永久相互阻塞。

对于递增和更新数字,还有interlocked operations 为您处理锁定,确保这些操作一次由一个线程执行。

只是为了好玩,我编写了这个控制台应用程序。它需要一个句子,将其分解为单词,然后使用多个线程将每个单词添加回一个新字符串并输出该字符串。

using System;
using System.Threading.Tasks;

namespace FunWithThreading
{
    class Program
    {
        static void Main(string[] args)
        {
            var sentence =
                "I am going to add each of these words to a string "
                + "using multiple threads just to see what happens.";
            var words = sentence.Split(' ');
            var output = "";
            Parallel.ForEach(words, word => output = output + " " + word);
            Console.WriteLine(output);
            Console.ReadLine();
        }
    }
}

我运行它的前两次,输出字符串正是我开始使用的。太好了,效果很好!然后我得到了这个:

I am going to add of these words to a string using multiple threads just to see what happens. each

然后我又运行了 20 次,并且无法重复错误。想象一下,如果这是一个真实的应用程序,并且即使我一遍又一遍地测试,像这样不可预测的事情发生了,我也无法让它再次发生。

所以重点并不是说多线程是邪恶的,只是为了了解其中的风险,只有在需要的时候才引入,然后再仔细考虑如何防止线程相互干扰。

【讨论】:

    【解决方案2】:

    回应 Luaan 的评论。我已将 endGameTrigger 作为 private static string endGameTrigger 放入 Handler 类中。将其设置为static 字段而不是本地方法变量允许handler 类的所有实例(每个线程)访问此变量的最新分配。非常感谢。

    【讨论】:

    • 查看我的评论 - 你不希望多个线程修改同一个字符串(或其他值),除非你使用锁来确保它们不会尝试同时修改它.奇怪、不可预测、不一致的事情可能而且将会发生。
    • 我的代码在任何时候都只会处理两个线程,并且只有第一个线程(由布尔值标识)将在更改值时进行操作。另一个线程只会访问变量以查找值,永远不会设置新值。所以在这种情况下,我的答案是一个修复。
    • 话虽如此!您对锁定的意见肯定很有趣,而且编码实践无疑要好得多,尽管我可能不需要,但我现在正在研究锁定。非常感谢您提醒我这个问题。
    • 一个线程读取而另一个线程写入并不重要。一个可以读,另一个在写。该修复程序在大多数情况下都会起作用,但随后不会。有些幕后发生的事情我们不知道或不考虑,只要我们的操作是单线程的,它们就不会影响我们。这就是为什么即使应用程序是多线程的,我们也需要确保代码的某些部分(例如读取和写入单个字符串)是单线程的。
    猜你喜欢
    • 2014-03-15
    • 2017-05-10
    • 1970-01-01
    • 1970-01-01
    • 2018-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多