我对编程很陌生,希望你能帮助我。我的任务是制作 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:UsersMikeDocumentsSomeFile1.txt");
t2.Start(@"C:UsersMikeDocumentsSomeFile2.txt");
t3.Start(@"C:UsersMikeDocumentsSomeFile3.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() + "rnLine from File: " + line);
}
}
}
catch (Exception ex)
{
Console.WriteLine("Exception Reading File: " + fileName.ToString() + "rnException: " + 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);
不返回值。ThreadStart
和ParameterizedThreadStart
委托的返回类型为 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);
}
简单的答案:你不能。您必须编写收集结果的逻辑代码。即使用全局变量来保持总和。但在更新总和时使用锁定。