如何将C#Parallel.For与线程本地存储引用类型一起使用

如何将C#Parallel.For与线程本地存储引用类型一起使用,c#,linq,parallel-processing,task-parallel-library,C#,Linq,Parallel Processing,Task Parallel Library,我正在寻找一个关于如何在C#中使用Parallel.for和reference类型的示例。我已经阅读了MSDN文档,我所能找到的都是使用值类型进行线程本地存储的示例。我正在尝试的代码如下所示: public string[] BuildStrings(IEnumerable<string> str1, IEnumerable<string> str2, IEnumerable<string> str3) { // This method aggrega

我正在寻找一个关于如何在C#中使用Parallel.for和reference类型的示例。我已经阅读了MSDN文档,我所能找到的都是使用值类型进行线程本地存储的示例。我正在尝试的代码如下所示:

public string[] BuildStrings(IEnumerable<string> str1, IEnumerable<string> str2, IEnumerable<string> str3)
{
    // This method aggregates the strings in each of the collections and returns the combined set of strings.  For example:
    // str1 = "A1", "B1", "C1"
    // str2 = "A2", "B2", "C2"
    // str3 = "A3", "B3", "C3"
    //
    // Should return:
    // "A1 A2 A3"
    // "B1 B2 B3"
    // "C1 C2 C3"
    //
    // The idea behind this code is to use a Parallel.For along with a thread local storage StringBuilder object per thread.
    // Don't need any final method to execute after each partition has completed.
    // No example on how to do this that I can find.

    int StrCount = str1.Count(); // str1, str2, and str3 guaranteed to be equal in size and > 0.
    var RetStr = new string[StrCount];
    Parallel.For<StringBuilder>(0, StrCount, () => new StringBuilder(200), (i, j, sb1) =>
    {
        sb1.Clear();
        sb1.Append(str1.ElementAt(i)).Append(' ').Append(str2.ElementAt(i)).Append(' ').Append(str3.ElementAt(i));
        RetStr[i] = sb1.ToString();
    }, (x) => 0);
    return RetStr;
}
该行现在生成错误:

错误“System.Collections.Generic.IEnumerable”不存在 包含“ElementAt”的定义和最佳扩展方法 超载 'System.Linq.Enumerable.ElementAt(System.Collections.Generic.IEnumerable, int)“”具有一些无效参数


因此,我不知道问题是什么。

事实证明,正确编译代码的问题是语法问题。如果微软发布了一个关于这个案例的例子,它真的会有所帮助。以下代码将正确生成和运行:

public string[] BuildStrings(IEnumerable<string> str1, IEnumerable<string> str2, IEnumerable<string> str3)
{
    // This method aggregates the strings in each of the collections and returns the combined set of strings.  For example:
    // str1 = "A1", "B1", "C1"
    // str2 = "A2", "B2", "C2"
    // str3 = "A3", "B3", "C3"
    //
    // Should return:
    // "A1 A2 A3"
    // "B1 B2 B3"
    // "C1 C2 C3"
    //
    // The idea behind this code is to use a Parallel.For along with a thread local storage StringBuilder object per thread.
    // Don't need any final method to execute after each partition has completed.
    // No example on how to do this that I can find.

    int StrCount = str1.Count(); // str1, str2, and str3 guaranteed to be equal in size and > 0.
    var RetStr = new string[StrCount];
    Parallel.For<StringBuilder>(0, StrCount, () => new StringBuilder(200), (i, j, sb1) =>
    {
        sb1.Clear();
        sb1.Append(str1.ElementAt(i)).Append(' ').Append(str2.ElementAt(i)).Append(' ').Append(str3.ElementAt(i));
        RetStr[i] = sb1.ToString();
        return sb1; // Problem #1 solved.  Signature of function requires return value.
    }, (x) => x = null); // Problem #2 solved.  Replaces (x) => 0 above.
    return RetStr;
}
public string[]BuildStrings(IEnumerable str1、IEnumerable str2、IEnumerable str3)
{
//此方法聚合每个集合中的字符串并返回组合的字符串集。例如:
//str1=“A1”、“B1”、“C1”
//str2=“A2”、“B2”、“C2”
//str3=“A3”、“B3”、“C3”
//
//应返回:
//“A1 A2 A3”
//“B1 B2 B3”
//“C1 C2 C3”
//
//这段代码背后的思想是使用Parallel.For以及每个线程的线程本地存储StringBuilder对象。
//不需要在每个分区完成后执行任何最终方法。
//我找不到关于如何做到这一点的例子。
int StrCount=str1.Count();//保证str1、str2和str3的大小相等且大于0。
var RetStr=新字符串[StrCount];
对于(0,StrCount,()=>新的StringBuilder(200),(i,j,sb1)=>
{
sb1.Clear();
sb1.Append(str1.ElementAt(i)).Append(“”).Append(str2.ElementAt(i)).Append(“”).Append(str3.ElementAt(i));
RetStr[i]=sb1.ToString();
return sb1;//问题#1已解决。函数签名需要返回值。
},(x)=>x=null);//问题#2已解决。替换上面的(x)=>0。
返回RetStr;
}
因此,正如Jon Skeet在评论中指出的,第一个问题是我的lambda方法无法返回值。因为我没有使用返回值,所以我没有输入一个-至少在最初是这样。当我输入return语句时,编译器显示了“ElementAt”静态方法的另一个错误-如上面的EDIT下所示

事实证明,编译器标记为问题的“ElementAt”错误与问题无关。这就提醒了我C++编译器的时候,编译器几乎没有C编译器的帮助。在C#中,将错误的行识别为错误是非常罕见的,但是从这个例子中可以看出,它确实发生了

第二个问题是线(x)=>0)。这一行是函数中的第5个参数,每个线程在完成所有工作后都会调用它。我最初尝试将其更改为(x)=>x.Clear。这导致生成错误消息:

只有赋值、调用、递增、递减、等待和新对象 表达式可以用作语句

“ElementAt”错误也仍然存在。因此,根据这条线索,我决定(x)=>0可能是导致真正问题的原因-减去一条错误消息。由于此时工作已完成,我将其更改为将StringBuffer对象设置为null,因为不再需要它。神奇的是,所有的“ElementAt”错误都消失了。在那之后,它建立并正确运行了

Parallel.For提供了一些很好的功能,但我认为微软最好重新考虑一些功能。任何时候一条线路引起问题时,都应将其标记为问题。这至少需要解决


如果微软能为Parallel提供一些额外的重写方法也会很好。因为这将允许返回void,并接受第5个参数的null值。实际上,我试着为它发送一个空值,它构建了。但是,因此发生了运行时异常。更好的方法是在不需要调用“线程完成”方法时为4个参数提供重写。

结果表明,正确编译代码的问题是语法问题。如果微软发布了一个关于这个案例的例子,它真的会有所帮助。以下代码将正确生成和运行:

public string[] BuildStrings(IEnumerable<string> str1, IEnumerable<string> str2, IEnumerable<string> str3)
{
    // This method aggregates the strings in each of the collections and returns the combined set of strings.  For example:
    // str1 = "A1", "B1", "C1"
    // str2 = "A2", "B2", "C2"
    // str3 = "A3", "B3", "C3"
    //
    // Should return:
    // "A1 A2 A3"
    // "B1 B2 B3"
    // "C1 C2 C3"
    //
    // The idea behind this code is to use a Parallel.For along with a thread local storage StringBuilder object per thread.
    // Don't need any final method to execute after each partition has completed.
    // No example on how to do this that I can find.

    int StrCount = str1.Count(); // str1, str2, and str3 guaranteed to be equal in size and > 0.
    var RetStr = new string[StrCount];
    Parallel.For<StringBuilder>(0, StrCount, () => new StringBuilder(200), (i, j, sb1) =>
    {
        sb1.Clear();
        sb1.Append(str1.ElementAt(i)).Append(' ').Append(str2.ElementAt(i)).Append(' ').Append(str3.ElementAt(i));
        RetStr[i] = sb1.ToString();
        return sb1; // Problem #1 solved.  Signature of function requires return value.
    }, (x) => x = null); // Problem #2 solved.  Replaces (x) => 0 above.
    return RetStr;
}
public string[]BuildStrings(IEnumerable str1、IEnumerable str2、IEnumerable str3)
{
//此方法聚合每个集合中的字符串并返回组合的字符串集。例如:
//str1=“A1”、“B1”、“C1”
//str2=“A2”、“B2”、“C2”
//str3=“A3”、“B3”、“C3”
//
//应返回:
//“A1 A2 A3”
//“B1 B2 B3”
//“C1 C2 C3”
//
//这段代码背后的思想是使用Parallel.For以及每个线程的线程本地存储StringBuilder对象。
//不需要在每个分区完成后执行任何最终方法。
//我找不到关于如何做到这一点的例子。
int StrCount=str1.Count();//保证str1、str2和str3的大小相等且大于0。
var RetStr=新字符串[StrCount];
对于(0,StrCount,()=>新的StringBuilder(200),(i,j,sb1)=>
{
sb1.Clear();
sb1.Append(str1.ElementAt(i)).Append(“”).Append(str2.ElementAt(i)).Append(“”).Append(str3.ElementAt(i));
RetStr[i]=sb1.ToString();
return sb1;//问题#1已解决。函数签名需要返回值。
},(x)=>x=null);//问题#2已解决。替换上面的(x)=>0。
返回RetStr;
}
因此,正如Jon Skeet在评论中指出的,第一个问题是我的lambda方法无法返回值。因为我没有使用返回值,所以我没有输入一个-至少在最初是这样。Whe
sb1.Append(str1.ElementAt(i)).Append(' ').Append(str2.ElementAt(i)).Append(' ').Append(str3.ElementAt(i));
public string[] BuildStrings(IEnumerable<string> str1, IEnumerable<string> str2, IEnumerable<string> str3)
{
    // This method aggregates the strings in each of the collections and returns the combined set of strings.  For example:
    // str1 = "A1", "B1", "C1"
    // str2 = "A2", "B2", "C2"
    // str3 = "A3", "B3", "C3"
    //
    // Should return:
    // "A1 A2 A3"
    // "B1 B2 B3"
    // "C1 C2 C3"
    //
    // The idea behind this code is to use a Parallel.For along with a thread local storage StringBuilder object per thread.
    // Don't need any final method to execute after each partition has completed.
    // No example on how to do this that I can find.

    int StrCount = str1.Count(); // str1, str2, and str3 guaranteed to be equal in size and > 0.
    var RetStr = new string[StrCount];
    Parallel.For<StringBuilder>(0, StrCount, () => new StringBuilder(200), (i, j, sb1) =>
    {
        sb1.Clear();
        sb1.Append(str1.ElementAt(i)).Append(' ').Append(str2.ElementAt(i)).Append(' ').Append(str3.ElementAt(i));
        RetStr[i] = sb1.ToString();
        return sb1; // Problem #1 solved.  Signature of function requires return value.
    }, (x) => x = null); // Problem #2 solved.  Replaces (x) => 0 above.
    return RetStr;
}
    public static ParallelLoopResult For<TLocal>(int fromInclusive, int toExclusive, Func<TLocal> localInit, Func<int, ParallelLoopState, TLocal, TLocal> body)
    {
        return Parallel.For(fromInclusive, toExclusive, localInit, body, localFinally: _ => { });
    }

        static void StringBuilderFor(int count, Action<int, ParallelLoopState, StringBuilder> body)
    {
        Func<int, ParallelLoopState, StringBuilder, StringBuilder> b = (i, j, sb1) => { body(i, j, sb1); return sb1; };
        For(0, count, () => new StringBuilder(200), b);
    }
        int StrCount = str1.Count(); // str1, str2, and str3 guaranteed to be equal in size and > 0.
        var RetStr = from i in Enumerable.Range(0, StrCount)
                     let sb1 = new StringBuilder(200)
                     select (sb1.Append(str1.ElementAt(i)).Append(' ').Append(str2.ElementAt(i)).Append(' ').Append(str3.ElementAt(i))).ToString();
        return RetStr.AsParallel().ToArray();