0

据我了解,当您在方法中使用引用类型作为参数时,堆栈上的值会被复制,因此形式参数指向与原始堆上相同的内存地址,因此会发生变化一旦你完成了该方法,它们就会被持久化。

这如何处理任务?我刚刚创建了 2 个新任务并传入了一个在 UI 线程上声明的数组。在其中一项新任务中所做的更改立即显示在第二项任务中。当我尝试通过 UI 线程更改输入(数组)时,两个新任务上的相同参数没有改变。我的印象是它应该有,因为它们都应该指向堆上的同一个内存位置??

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace TasksAndMemory
{
    class Program
    {
        private static ManualResetEvent mre = new ManualResetEvent(false);

        static void Main(string[] args)
        {
            int[] readToEnd = new int[2];
            int[] data = new int[] { 1, 2, 3, 4, 5, 6 };
            int valueType = 5;


            int pageCounter = 1;


            Task[] tasks = new Task[2];


            for (int x = 1; x < 3; x++)
            {
                //Needed due to closure problem
                int i = x;

                tasks[i-1] = Task.Factory.StartNew(() =>
                {
                    SpecialMethod(data, readToEnd, i, valueType);
                });
            }

            while(pageCounter < 4)
            {
                if (readToEnd[0] == 1 && readToEnd[1] == 1)
                {
                    //Sets the state of the event to nonsignaled, causing threads to block
                    mre.Reset();
                    int[] temp = new int[] { 7, 8, 9, 10, 11, 12 };
                    data = temp;
                    readToEnd[0] = 0;
                    readToEnd[1] = 0;

                    //Sets the state of the event to signaled, allowing one or more waiting threads to proceed.
                    mre.Set();
                    pageCounter++;
                }
            }
            Console.ReadLine();
        }

        public static void SpecialMethod(int[] historicalData, int[] readToEnd, int taskNumber, int valueTy)
        {
            int[] temp = new int[] { 100, 200, 300, 400, 500, 600 };

            for (int x = 0; x <= historicalData.Length; x++)
            {
                if (x == historicalData.Length)
                {
                    readToEnd[taskNumber-1] = 1;
                    mre.WaitOne();
                    x = 0;
                 }
                else
                {
                    valueTy++;
                    temp[x] = temp[x] + taskNumber;
                }
            }
        }
    }
}
4

2 回答 2

2

您创建一个数组:

int[] data = new int[] { 1, 2, 3, 4, 5, 6 };

然后,您将对该数组的引用的副本(当前存储在 中)作为参数传递给(通过原始代码中的捕获,但不重要,它仍然只是一个副本)。dataSpecialMethod

在 内SpecialMethod,参数int[] historicalData将收到对该原始数组的引用的副本。

此后,导致变量被重新分配的任何事情(与对由引用data数组中的数据所做的更改相反)对由其原始引用制作的任何副本都没有影响- 它们仍在引用原始数组。data

我不清楚您在线程之间传递数据方面的实际要求是什么,因此我无法提出任何坚定的建议。我通常会尽量避免使用原始数组。

于 2012-11-12T15:45:52.060 回答
1

您的分析一开始似乎是正确的,但您的结论却不正确。

您有一个引用类型(一个数组),并按值将其传递给一个方法(这是默认值)。这意味着对位于堆上的该数组的引用被复制

因为变量 inSpecialMethodMain都具有相同的引用,所以更改它们引用的值将被两个变量“看到”。

这仅适用于您对数组进行变异的情况。这就是您对 . 所做的事情readToEnd,这就是为什么处理它的代码部分按您的预期工作的原因。

data另一方面,您不会改变数组,只需将新数组分配给变量。那是在更改引用,而不是它引用的对象,这就是您遇到问题的原因。

至于解决方案,有几个。首先,您可以更改代码以改变数组而不是分配新的;只需更改现有值。如果您需要更改元素的数量,请考虑使用 aList而不是数组。

另一种选择是,而不是传递一个数组,而是添加另一层间接。您可以创建一个具有数组属性的新类,将该类型的对象传递给SpecialMethod,然后您可以更改该对象的属性并查看它在两个位置的反映。你可以使用这样的东西来涵盖一般情况:

public class Wrapper<T>
{
    public T Value { get; set; }
}
于 2012-11-12T15:46:05.310 回答