繁体   English   中英

从线程返回值?

[英]Return value from thread?

我是编程的新手,希望你能帮助我。 我的任务是制作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());

    }

.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());
}

试试这个......

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);
        }
    }

}

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

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

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

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

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();
}

现在,让我们来看看如果我们被允许使用任务,我们可以如何改变它:

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();
}

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

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

可能是某种形式:

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);

线程不返回值。 ThreadStartParameterizedThreadStart委托具有返回类型void

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

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关键字确保多个线程不能同时执行代码。 如果你不使用lock ,结果可能是错误的。

由于您对每个文件使用不同的方法,您当然可以使用不同的结果变量。 那你就不需要锁了。 但是如果你有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);
}

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM