在sking this question之后,我仍然有一个问题。
我得到了两个很好的答案,但我仍然无法找到 这实际发生的方式:(如何发生故障情况):
我将从一个例子开始:
public void Do(string [] g)
{
g=null; //<========
}
void Main()
{
var t=new string[3];
t[0]="1"; t[1]="1"; t[2]="1";
Do( t);
Console.WriteLine ( t.Length);
}
标记的行可以在不同的线程中执行,但每个线程都有自己的 g
变量!
(请记住,我无法向arrray添加项目。因为创建时会创建数组长度)
无论我如何处理函数 Do
- (无论在什么线程中),Console.Writeline结果始终为3
(除非使用{ {1}})。
让我们看看真实的代码:
ref
我的意思是:一旦我在#{1}}的线程#1上,这个线程将永远有长度为3的数组。
如果另一个线程想要破坏/更改数组长度(我无法理解如何,导致数组具有固定长度,他所能做的只是做它{ {1}}) - 它将有一个不同的指针地址副本。
我必须在这里遗漏一些东西。
我错过了什么?
其他线程可以执行哪些代码会导致错误?(假设我们不复制数组)。
答案 0 :(得分:6)
我在最初的帖子中找到了Jon Skeet和Eric Lippert的答案,我相信你错误地解释了他们的答案。他们担心如果你不将字符串复制到新数组,有人会在{"1","2","3"}
首次评估输入并执行之前将{"a very very", "long string", "(but much longer, yes?"}
更改为ConcatString
。串联。
请注意,第12行不处理输入数组的长度,而是处理最终输出字符串的长度。所有这些检查都处理了会话的结果,而不是输入数组。
答案 1 :(得分:4)
其他线程可以执行哪些代码会导致错误?
string[] data = { "1", "2", "3" };
ThreadPool.QueueUserWorkItem( () => { data[0] = "one"; } );
string total = String.Concat(data);
如果没有中间数组,就会出现竞争条件,在将的长度添加到data[0]
之后,线程可以更改totalLength
,但之前对ConcatArray()
的(可能是不安全的)呼叫。然后,低级方法将5个字符复制到大小为3的缓冲区。
答案 2 :(得分:2)
将数组引用传递给方法,因此每个线程(在本例中)都有自己的引用g。每个线程都可以更改自己的引用所指向的内容(例如,g = null),但是当您访问g中的项目时,例如g [0],两个线程都将访问相同的项目。
所以,问题是g[0] = null
,而不是g = null
。
答案 3 :(得分:1)
以下行确保数据一致性,以防在其他线程中修改数组内容
#11 strArray[i] = (str == null) ? Empty : str;
#12 totalLength += strArray[i].Length;
#18 return ConcatArray(strArray, totalLength);
比赛仍然存在,但在线号之前。 11,但如果使用原始数组(如果数据损坏),可能会将错误信息传递给ConcatArray
函数。
答案 4 :(得分:1)
John Skeet和Eric Lippert的意思是,如果在计算长度后数组内容可能会发生变化,那么计算总字符串长度会导致不同的结果。因为concat操作是在非托管代码中完成的,所以如果之后的字符串长度发生了变化,就会发生overuns。
为了说明这里的问题是他们所讨论的问题的重演:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static string[] arr = new string[] { "1", "2", "3" };
static string[] arr2 = new string[] { "1111", "2222222", "2222222222222222222222" };
[MTAThread]
public static void Main()
{
ThreadStart[] funcs = Enumerable.Range(0, Environment.ProcessorCount * 4).Select(i => new ThreadStart(MutateArray)).ToArray();
foreach (var func in funcs)
new Thread(func).Start();
Console.ReadLine();
}
static void MutateArray()
{
Random rand = new Random();
while (true)
{
int i = rand.Next(arr.Length);
// swap array contents with contents from another array so arr will always
// contain a mixture of arr and arr2 without knowing at which point of time which contents
// are inside it.
lock (arr)
{
string tmp = arr[i];
arr[i] = arr2[i];
arr2[i] = tmp;
}
Do(arr);
}
}
static void Do(string[] g)
{
AllocateBufferWithRightLength(g, StrLen(g));
}
static void AllocateBufferWithRightLength(string[] g, int strLen)
{
int newLen = StrLen(g);
if (strLen != newLen)
{
throw new Exception("Invariant broken");
}
}
static int StrLen(string[] g)
{
int strLen = 0;
foreach (var str in g)
{
if (str != null)
strLen += str.Length;
}
Thread.Sleep(1);
return strLen;
}
}
}