如何在原始线程上更改传递给在单独线程上运行的函数的值?

时间:2010-02-11 15:27:32

标签: c# .net multithreading

我创建了一个抽象类,它实现了一个在单独的线程上运行另一个抽象方法的方法,如下所示:

 public abstract class ATest
    {
        Thread t;
        protected String status;
        public void Start()
        {
            ThreadStart ts = new ThreadStart(PerformTest);
            t = new Thread(ts);
            t.Start();

        }

        public ATest(String status)
        {
            this.status=status;
        }
        public abstract void PerformTest();
    }

这个想法是从ATest派生的类只实现了PerformTest方法。因此,任何客户端都可以调用Start()方法在其自己的线程上的PerformTest()内启动操作。派生自ATest的类可能如下:

class ConcreteTest:ATest
    {
        public ConcreteTest(String status):base(status)
        {

        }

        public override void PerformTest()
        {
            // Do some things...
            // And some more...
            status = "changed";
        }

    }

创建ConcreteTest对象时,我想传入一些对象(在示例中为String)。当PerformTest()在其单独的线程上运行时,将根据PerformTest()中的操作结果更改此对象的状态。使用上述类的示例:

class Program
{
    static void Main(string[] args)
    {
        String isPassed = "original";
        ATest test = new ConcreteTest(isPassed);
        test.Start();
        Console.WriteLine(isPassed); // Prints out "original"
    }
}

所以我将isPassed设置为“originial”并将其传递给ConcreteTest,后者在一个单独的线程上将值更改为“changed”。因此,当我打印出isPassed时,我希望将值设置为“已更改”,但事实证明它不是。我想这与在最初创建它的另一个线程上更改的值有关。任何人都可以向我解释为什么我会得到这种行为,也许我可以做些什么来实现我正在寻找的功能(即更改isPassed在单独的线程上,以便当线程完成时,控制台将打印出来“改变”?

2 个答案:

答案 0 :(得分:1)

字符串是不可变的。

当您编写status = "changed"时,您不会更改任何现有的字符串对象 相反,您正在更改status字段以指代完全不同的字段 String实例。

status字段与isPassed方法中的Main变量无关。当您编写new ConcreteTest(isPassed)时,您将isPassed变量的传递给构造函数。
该参数与传递给它的变量无关,除了现在,它们碰巧引用同一个对象。您的status字段也是如此。

最简单的方法是定义一个持有者类型,如下所示:

class Holder<T> { public T Value { get; set; } }

如果您愿意,可以添加隐式强制转换和构造函数。

您也可以通过公开基类中的Status属性并在test.Status中编写Main来执行此操作。


你也有一个不太明显的问题 您的代码中没有任何内容强制它等待其他线程完成,因此您的Console.WriteLine可能会在另一个线程分配字段之前运行。

答案 1 :(得分:0)

通常,您希望将结果存储在ConcreteTest中,当任务完成时,请阅读ConcreteTest中的值,不要尝试将其推回原始位置。如果你真的想要推回它,你可以使用回调和lambda这样做。

class ConcreteTest:ATest
{
    public ConcreteTest(Action<string> statusResponder):base(statusResponder)
    {

    }

    public override void PerformTest()
    {
        // Do some things...
        // And some more...
        statusResponder("changed");
    }

}

class Program
{
    static void Main(string[] args)
    {
        String isPassed = "original";
        ATest test = new ConcreteTest(status => isPassed = status);
        test.Start();
        // Need to add a way to wait for task to be done here
        Console.WriteLine(isPassed); // Prints out "original"
    }
}