1

我对编程很陌生,希望你能帮助我。我的任务是创建 3 个不同的线程来读取具有给定 int 的 txt 文件。然后它必须打印出这些值的总和。我想从我制作的三个线程中访问 int 。我怎样才能做到这一点?

这是我的一些代码:

class Program
{
    static void Main()
    {

        Thread t1 = new Thread(ReadFile1);
        Thread t2 = new Thread(ReadFile2);
        Thread t3 = new Thread(ReadFile3);
        t1.Start();
        t2.Start();
        t3.Start();

        System.Console.WriteLine("Sum: ");
        Console.WriteLine();                                                                                        

        Console.WriteLine("");
        System.Console.ReadKey();                                                                                                 

    }

    public static void ReadFile1()
    {

        System.IO.StreamReader file1 = new System.IO.StreamReader({FILEDESTINATION});        
        int x = int.Parse(file1.ReadLine());

    }
4

6 回答 6

2

.NET 中的任务系统使这变得非常容易。在几乎所有情况下,您都应该更喜欢它而不是原始线程。对于您的示例:

var t1 = Task.Run(() => ReadFile(path1));
var t2 = Task.Run(() => ReadFile(path2));
var t3 = Task.Run(() => ReadFile(path3));

Console.WriteLine("Sum: {0}", t1.Result + t2.Result + t3.Result);

static int ReadFile(string path) {
    using(var file = new StreamReader(path))      
        return int.Parse(file.ReadLine());
}
于 2013-10-27T22:40:46.090 回答
1

试试这个...

class Program
{

    static int? Sum = null;
    static Object lockObject = new Object();

    static void Main()
    {
        Thread t1 = new Thread(ReadFile);
        Thread t2 = new Thread(ReadFile);
        Thread t3 = new Thread(ReadFile);
        t1.Start(@"C:\Users\Mike\Documents\SomeFile1.txt");
        t2.Start(@"C:\Users\Mike\Documents\SomeFile2.txt");
        t3.Start(@"C:\Users\Mike\Documents\SomeFile3.txt");

        t1.Join();
        t2.Join();
        t3.Join();

        if (Sum.HasValue)
        {
            System.Console.WriteLine("Sum: " + Sum.ToString());
        }
        else
        {
            System.Console.WriteLine("No values were successfully retrieved from the files!");
        }
        Console.WriteLine("");
        Console.Write("Press Enter to Quit");
        System.Console.ReadLine();
    }

    public static void ReadFile(Object fileName)
    {
        try
        {
            using (System.IO.StreamReader file1 = new System.IO.StreamReader(fileName.ToString()))
            {
                int x = 0;
                string line = file1.ReadLine();
                if (int.TryParse(line, out x))
                {
                    lock (lockObject)
                    {
                        if (!Sum.HasValue)
                        {
                            Sum = x;
                        }
                        else
                        {
                            Sum = Sum + x;
                        }
                    }
                }
                else
                {
                    Console.WriteLine("Invalid Integer in File: " + fileName.ToString() + "\r\nLine from File: " + line);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception Reading File: " + fileName.ToString() + "\r\nException: " + ex.Message);
        }
    }

}
于 2013-10-27T21:54:37.730 回答
1

从@pescolino 解释得很清楚的答案开始,我们可以做出一些改进。首先,如果我们假设您的讲师确实希望您使用实际的“线程”而不是 Tasks *,我们仍然可以通过使用Interlocked库而不是手动锁定对象来改进代码。这将为我们提供更好的性能,以及(更重要的是)更简单的代码。

private static void ReadIntFromFile(string filename)
{
    string firstLine = System.IO.File.ReadLines(filename).First();

    Interlocked.Add(ref result, int.Parse(firstLine));
}

现在,我不知道你是否已经介绍过 LINQ——我知道有时教师不喜欢学生使用他们还没有学过的工具——但如果你被允许,我们可以使用 main 方法简单得多:

private static void Main()
{
    var files = new[]{"File1.txt", "File2.txt", "File3.txt"};
    var threads = files.Select(f => new Thread(() => ReadIntFromFile(f))).ToList();
    threads.ForEach(t => t.Start());
    threads.ForEach(t => t.Join());

    Console.Write("Sum: {0}", result);
    console.ReadLine();
}

现在,让我们看看如果我们最终被允许使用 Tasks,我们可以如何改变它:

private static void Main()
{
    var files = new[]{"File1.txt", "File2.txt", "File3.txt"};
    var tasks = files.Select(f => Task.Factory.StartNew(() => ReadIntFromFile(f)));
    Task.WaitAll(tasks.ToArray());
    Console.Write("Sum: {0}", result);
    Console.ReadLine();
}

但是您知道,一旦我们使用 LINQ 和 TPL,更“功能性”的编程方法会变得更加有利。换句话说,与其将 ReadIntFromFile 方法添加到全局变量(ick!),不如让它返回它读取的值:

private static int ReadIntFromFile(string filename)
{
    string firstLine = System.IO.File.ReadLines(filename).First();
    return int.Parse(firstLine);
}

现在看看我们可以用 main 方法做什么:

private static void Main()
{
    var files = new[]{"File1.txt", "File2.txt", "File3.txt"};
    int result = files.AsParallel().Sum(f => ReadIntFromFile(f));
    Console.Write("Sum: {0}", result);
    Console.ReadLine();
}

如果我们使用所有可用的工具,看看并行代码有多简单?

*任务并不总是在单独的线程中运行——它们通常共享相同的线程。

于 2013-10-28T03:15:10.180 回答
0

可能是这样的:

public static void ReadFile1(ref int? x)
{
    System.IO.StreamReader file1 = new System.IO.StreamReader( {FILEDESTINATION});
    x = int.Parse(file1.ReadLine());
}

并调用它

int? res1 = null;
Thread t1 = new Thread(()=>ReadFile1(ref res1));
//...
t1.Start();
t1.Join();

System.Console.WriteLine("Sum: " + res1);
于 2013-10-27T20:55:40.073 回答
0

线程不返回值。和代表ThreadStartParameterizedThreadStart返回类型void

要使用线程执行此操作,您需要将结果存储在某处。如果它是共享变量,则lock在更新此值期间需要 a 以避免冲突:

private object lockObj = new object();
private int result;

private static void Main()
{
    result = 0;

    Thread t1 = new Thread(() => ReadIntFromFile("File1.txt"));
    Thread t2 = new Thread(() => ReadIntFromFile("File2.txt"));
    Thread t3 = new Thread(() => ReadIntFromFile("File3.txt"));
    t1.Start();
    t2.Start();
    t3.Start();

    // don't forget to call Join to wait for the end of each thread
    t1.Join();
    t2.Join();
    t3.Join();

    Console.Write("Sum: {0}", result);
    console.ReadLine();
}

private void ReadIntFromFile(string filename)
{
    string firstLine = System.IO.File.ReadLines(filename).First();

    lock (lockObj)
    {
        result += int.Parse(firstLine);
    }
}

lock关键字确保代码不能被多个线程同时执行。如果您不使用 alock结果可能是错误的。

由于您对每个文件使用不同的方法,因此您当然可以使用不同的结果变量。那你就不需要锁了。但是如果你有 100 个文件呢?您可能不想编写 100 个方法。

一个更简单的方法是使用TPL(从 .NET 4 开始)。任务可以具有返回类型并且更易于管理。我还更改了方法以允许任意数量的文件:

private static void Main()
{
    var sum = SumValuesFromFiles("File1.txt", "File2.txt", "File3.txt");

    Console.Write("Sum: {0}", sum);
    Console.ReadLine();
}

private static int SumValuesFromFiles(params string[] files)
{
    Task<int>[] tasks = new Task<int>[files.Length];
    for (int i = 0; i < files.Length; i++)
    {
        // use a local copy for the parameter because i might get changed before the method is called
        string filename = files[i];

        tasks[i] = Task.Factory.StartNew(() =>
                                         {
                                             string firstLine = System.IO.File.ReadLines(filename).First();
                                             return int.Parse(firstLine);
                                         });
    }

    Task.WaitAll(tasks);

    return tasks.Sum(t => t.Result);
}
于 2013-10-27T22:52:10.347 回答
-1

简单的回答:你不能。您必须编写收集结果的逻辑。即使用一个全局变量来保持总和。但是在更新总和时使用锁定。

于 2013-10-27T20:03:38.803 回答