请参阅以下代码。
class AddParams
{
public int a, b;
public AddParams(int numb1, int numb2)
{
a = numb1;
b = numb2;
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("ID of thread in 1: {0}",
Thread.CurrentThread.ManagedThreadId);
AddAsync();
Console.ReadLine();
}
private static async Task AddAsync()
{
Console.WriteLine("***** Adding with Thread objects *****");
Console.WriteLine("ID of thread in Main(): {0}",
Thread.CurrentThread.ManagedThreadId);
AddParams ap = new AddParams(10, 10);
await Sum(ap);
Console.WriteLine("Other thread is done!");
}
static async Task Sum(object data)
{
await Task.Run(() =>
{
if (data is AddParams)
{
Console.WriteLine("ID of thread in Add(): {0}",
Thread.CurrentThread.ManagedThreadId);
AddParams ap = (AddParams)data;
Console.WriteLine("{0} + {1} is {2}",
ap.a, ap.b, ap.a + ap.b);
}
});
}
}
结果是这样的:
***** Adding with Thread objects *****
ID of thread in Main(): 1
ID of thread in Add(): 3
10 + 10 is 20
Other thread is done!
我得到的结果与我的预期不同。
请通过给出正确的概念来纠正我的假设。
1。
我假设Main()是在主线程(例如Thread1)上调用的。
2。
调用AddAsync(),但是此方法带有异步标记,因此可能在辅助线程(例如Thread2)上调用此方法。
但是AddAsync()方法中以下代码的结果表明Thread1与我期望的Thread2不同:
Console.WriteLine("ID of thread in Main(): {0}",Thread.CurrentThread.ManagedThreadId);
3。
调用Sum()方法。但是通过使用await关键字进行修饰,将在辅助线程(例如Thread3)上调用Sum()方法。
在Sum()方法内部,在辅助线程(例如Thread4)上调用Run()方法。
我现在这样理解:
当我进行异步编程时,创建新线程取决于CLR。 CLR尽可能创建多线程,但是CLR也可以在单个线程上处理异步任务,只需在单个线程上同时异步处理多个任务即可。
我只是假设一个简单的情况,每次尝试异步任务时,CLR都会创建一个新线程。
我知道这个主题很难描述,所以如果用图纸进行解释会更好。
其他问题。
1。
AddAsync()方法中的以下代码是否真的指示“ Main()”中线程的ID?对我来说,使用“ AddAsync()中的线程ID”会更合适。
Console.WriteLine("ID of thread in Main(): {0}", Thread.CurrentThread.ManagedThreadId);
参考方案
原始答案
我假设Main()是在主线程(例如Thread1)上调用的。
是的,Main
将在主线程上运行。
调用AddAsync(),但是此方法带有异步标记,因此可能在辅助线程(例如Thread2)上调用此方法。但是AddAsync()方法中以下代码的结果表明Thread1与我期望的Thread2不同:
Console.WriteLine("ID of thread in Main(): {0}",Thread.CurrentThread.ManagedThreadId);
否。如果您未达到await
,该代码将不会切换线程。
static void Main(string[] args) // <- main thread
{
Console.WriteLine("ID of thread in 1: {0}",
Thread.CurrentThread.ManagedThreadId); // <- main thread
AddAsync(); // <- main thread (note: you are not awaiting here)
Console.ReadLine();
}
private static async Task AddAsync()
{
Console.WriteLine("***** Adding with Thread objects *****"); // <- main thread
Console.WriteLine("ID of thread in Main(): {0}", // <- main thread
Thread.CurrentThread.ManagedThreadId);
AddParams ap = new AddParams(10, 10); // <- main thread
await Sum(ap); // <- ok, we cannot continue.
// Add `Sum(ap)` to pending stuff.
// When Sum(ap) is done we resume here, potentially in another thread.
// The main thread is now free to do pending stuff.
// Turns out `Sum(ap)` is pending, run it on the main thread.
Console.WriteLine("Other thread is done!");
}
调用Sum()方法。但是通过使用await关键字进行修饰,将在辅助线程(例如Thread3)上调用Sum()方法。
它可能会或可能不会在同一线程中运行。 Sum
很可能会在主线程中运行,因为当主线程正在等待Sum
时,我们需要一个线程来运行Sum
,因此主线程可用。
如果在Sum
的开头添加以下行,我希望它说出与主线程相同的id:
Console.WriteLine("ID of thread in Sum(): {0}", Thread.CurrentThread.ManagedThreadId);
在Sum()方法内部,在辅助线程(例如Thread4)上调用Run()方法。
是的,Task.Run
将使用另一个线程,默认情况下是ThreadPool
中的一个。注意:我默认说,因为这取决于TaskScheduler
,默认情况下将使用ThreadPool
。
我现在这样理解:当我进行异步编程时,创建新线程取决于CLR。 CLR尽可能创建多线程,但是CLR也可以在单个线程上处理异步任务,只需在单个线程上同时异步处理多个任务即可。我只是假设一个简单的情况,每次尝试异步任务时,CLR都会创建一个新线程。
它不会每次都启动新线程。 async/await
不是Thread
周围的语法糖,而是Task
和延续处。 Task
已被设计为避免使用不必要的新线程(例如a Task
may run inline)或使用ThreadPool
。
AddAsync()方法中的以下代码是否真的指示“ Main()”中线程的ID?对我来说,使用“ AddAsync()中的线程ID”会更合适。
Console.WriteLine("ID of thread in Main(): {0}", Thread.CurrentThread.ManagedThreadId);
如上面代码中的注释所示,是的,这是主线程。
达到await Task.Run...
后,主线程将处于空闲状态,因为它必须等待任务完成。恢复时,它将返回到AddAsync
,运行Console.WriteLine("Other thread is done!");
,然后返回到运行Main
的Console.ReadLine();
。如果在Main
调用之前在Console.ReadLine
中添加以下行,则将看到主线程的ID:
Console.WriteLine("ID of thread before ReadLine: {0}",
Thread.CurrentThread.ManagedThreadId);
如您所见,您的代码不需要并行处理。除了使用Task.Run
之外,它还可以在单个线程中运行。勘误表:进一步检查发现存在并行性,只是不那么明显...请参阅扩展的答案。
扩展答案
经过二读后,我怀疑您期望对AddAsync
的调用并行运行。就像我在上面说的那样,您并没有等待它,在这种情况下,它像常规的同步调用一样运行。
如果要并行运行AddAsync
,建议使用Task.Run
,例如:
Task.Run((Func<Task>)AddAsync);
这样,AddAsync
将不再在主线程中运行。主线程将前进到Console.ReadLine
,甚至可能在AddASync
之前结束。请注意,执行将在主线程结束后立即结束。
当然,AddAsync
很快,我建议您await
几个Task.Delay
给您一些时间来敲击该键。
在您问之前,我先问一个问题:Task.Delay
如何工作? -内部结构的简化解释(至少在Windows上是),它将要求操作系统超时。当操作系统看到时间已到时,它将调用程序以通知超时已完成。这样,Task.Delay
无需使用线程即可运行。
这是Task
的另一种类型,其中不必运行代码,因此不需要占用线程。我们可以将这种Taks称为诺言。另一个示例是从文件读取,例如:
using (var reader = File.OpenText("example.txt"))
{
var fileText = await reader.ReadToEndAsync();
// ...
}
在这种情况下,读取文件的操作将不需要您的线程之一。在内部,操作系统将要求驱动程序将数据复制到RAM缓冲区并通知完成时间(由于DMA要求对CPU的干预最少,因此在现代硬件中会发生这种情况),因此该处不使用任何线程。
同时,调用线程可以自由地做其他事情。如果您有这样的多种操作(例如,您可能正在从文件中读取数据,将数据发送到网络等),则它们可以并行发生,而无需使用线程,并且当其中一个线程完成时,便可以执行代码将在您可用的线程之一中恢复。
还要考虑的另一件事是,如果您正在使用UI线程,则工作原理会完全不同。
在窗口中,许多操作都从message queue开始。无需担心其工作原理,只需要说主线程将花费大量时间等待输入事件(单击,按键等)。
正如您将在扩展答案的结尾看到的那样,方法可能会在其他线程中继续执行。但是,UI线程仅仅是可以与UI进行交互的线程。因此,让UI代码在其他线程中运行不是很好。
为了解决该问题,在UI线程中,await
将使该线程继续在消息队列上工作。此外,它将消息继续发送到队列中,以允许UI线程接收消息。存档的方法是对UI线程使用不同的TaskScheduler
。
这也意味着,如果您在UI环境中,并且将await
用于承诺任务,它将使它保持对输入事件的响应。这样可以节省您使用BackgroundWorker
的时间...好吧,除非您需要一些需要大量CPU时间的内容,否则您将需要使用Task.Run
,调用ThreadPool
,使用BackgroundWorker
或启动Thread
。
你的问题
因此,我可以在这段代码中说,仅Task.Run()创建一个新线程吗?和async和await关键字不会创建新线程吗?
不,Task.Run
正在使用另一个线程,但未创建它。默认情况下,它退回到ThreadPool
。
ThreadPool
是做什么的?好吧,它保留了一小组线程,可用于按需运行操作(例如,运行Task
),一旦操作完成,该线程将返回ThreadPool
,在该线程中它将保持空闲状态,直到您执行再次需要它。对于摘要:ThreadPool
回收线程。
在AddAsync()内部的此调用点“ await Sum(ap)”处,主线程仍在调用Sum(ap),对
是的,它仍然是主线程。将在下面对此进行更详细的介绍。
转到Sum()方法,仍在主线程上处理的代码突然由Task.Run()遇到。在“ Task.Run()”这一点上,创建了一个新线程,并在新线程上执行了lambda表达式代码吗?
就像我在上面说的,Task.Run
不必创建新线程。它将要求ThreadPool
在其线程之一上运行该操作。这些ThreadPool
线程可以执行一次操作,因此您不必最终创建大量的Thread
,而仅回收其中的几个。
因此,是的,lambda中的代码将在不同的Thread
中运行,但并不是为此而创建的。
当处理“ Task.Run()”时,正在等待Task.Run()方法结果的调用线程(主线程)的状态是什么?是被阻止还是未被阻止?
首先请注意,有两个选项可以等待Task.Run
。您可以使用await
或使用Task.Wait
。
await
将:
“暂停”该方法的执行。
获取或创建方法的不完整Task
。
将继续添加到Task
以“恢复”该方法。或者,如果没有其他可运行的内容,则继续操作会将未完成的Task
设置为完成。
将不完整的Task
返回给呼叫者。
Task.Wait
将:
阻塞线程,直到Task
完成。
现在,我将更加缓慢地检查代码...
首先,再次同步部分:
static void Main(string[] args) // <-- entry point, main thread
{
Console.WriteLine("ID of thread in 1: {0}",
Thread.CurrentThread.ManagedThreadId); // <-- main thread
AddAsync(); // <-- main thread. You are not awaiting, this is a sync call.
Console.ReadLine();
}
此时,我们将创建一个不完整的Task
,我将其称为AddAsync Task
。这就是AddAsync
返回的内容(不是您将使用它,而是忽略它)。
然后主线程进入AddAsync
:
private static async Task AddAsync() // <-- called from `AddAsync()`
{
Console.WriteLine("***** Adding with Thread objects *****"); // <-- main thread
Console.WriteLine("ID of thread in Main(): {0}",
Thread.CurrentThread.ManagedThreadId); // <-- main thread
AddParams ap = new AddParams(10, 10); // <-- main thread
await Sum(ap); // <-- shenanigans!!!
Console.WriteLine("Other thread is done!");
}
让我重构一下,真正的快速...
private static async Task AddAsync() // <-- called from `AddAsync()`
{
Console.WriteLine("***** Adding with Thread objects *****"); // <-- main thread
Console.WriteLine("ID of thread in Main(): {0}",
Thread.CurrentThread.ManagedThreadId); // <-- main thread
AddParams ap = new AddParams(10, 10); // <-- main thread
var z = Sum(ap); // <-- shenanigans!!!
await z;
Console.WriteLine("Other thread is done!");
}
接下来发生的是对Sum
的调用。此时,为Task
创建了一个新的不完整的Sum
。我将其称为Sum Task
。
接下来,主线程进入Sum
:
static async Task Sum(object data) // <-- called from `await Sum(ap)`
{
await Task.Run(() =>
{
if (data is AddParams)
{
Console.WriteLine("ID of thread in Add(): {0}",
Thread.CurrentThread.ManagedThreadId);
AddParams ap = (AddParams)data;
Console.WriteLine("{0} + {1} is {2}",
ap.a, ap.b, ap.a + ap.b);
}
});
}
还有更多的恶作剧...让我重构该代码...
static async Task Sum(object data) // <-- called from `await Sum(ap)`
{
Action y = () =>
{
if (data is AddParams)
{
Console.WriteLine("ID of thread in Add(): {0}",
Thread.CurrentThread.ManagedThreadId);
AddParams ap = (AddParams)data;
Console.WriteLine("{0} + {1} is {2}",
ap.a, ap.b, ap.a + ap.b);
}
};
var x = Task.Run(y);
await x;
}
上面的代码等同于我们所拥有的。请注意,您可以使用x.Wait()
来阻塞主线程。我们没有那样做...
static async Task Sum(object data) // <-- called from `await Sum(ap)`
{
Action y = () =>
{
if (data is AddParams)
{
Console.WriteLine("ID of thread in Add(): {0}",
Thread.CurrentThread.ManagedThreadId);
AddParams ap = (AddParams)data;
Console.WriteLine("{0} + {1} is {2}",
ap.a, ap.b, ap.a + ap.b);
}
}; // <-- Action created in main thread
var x = Task.Run(y); // <-- main threat: create a new Task x with the action y
// start the new Task in a thread from the thread pool
await x;
}
现在,有趣的部分...
static async Task Sum(object data)
{
Action y = () =>
{
if (data is AddParams) // <-- second thread
{
Console.WriteLine("ID of thread in Add(): {0}",
Thread.CurrentThread.ManagedThreadId);
AddParams ap = (AddParams)data;
Console.WriteLine("{0} + {1} is {2}",
ap.a, ap.b, ap.a + ap.b);
}
};
var x = Task.Run(y);
await x; // <-- Add a continuation to x
// so that when it finished, it will set the Sum Task to completed
}
现在,方法Sum
返回(不完整的Sum Task
)
private static async Task AddAsync()
{
Console.WriteLine("***** Adding with Thread objects *****");
Console.WriteLine("ID of thread in Main(): {0}",
Thread.CurrentThread.ManagedThreadId);
AddParams ap = new AddParams(10, 10);
var z = Sum(ap); // <-- main thread, z is now the incomplete Sum Task
await z; // <-- Add a continuation to z
// so that when it finished, it will resume `AddAsync`
// `AddAsync` is "paused" now.
// main thread returns the incomplete Async Task
Console.WriteLine("Other thread is done!");
}
现在,方法AddAsync
返回(不完整的AddAsync Task
)。我想在这里强调一下:方法AddSync
尚未完成,但返回的状态不完整。
static void Main(string[] args)
{
Console.WriteLine("ID of thread in 1: {0}",
Thread.CurrentThread.ManagedThreadId);
AddAsync();
Console.ReadLine(); // <-- main thread
}
同时,第二个线程完成...
static async Task Sum(object data)
{
Action y = () =>
{
if (data is AddParams) // <-- second thread
{
Console.WriteLine("ID of thread in Add(): {0}",
Thread.CurrentThread.ManagedThreadId); // <-- second thread
AddParams ap = (AddParams)data; // <-- second thread
Console.WriteLine("{0} + {1} is {2}",
ap.a, ap.b, ap.a + ap.b); // <-- second thread
}
};
var x = Task.Run(y);
await x;
}
并触发我们添加到x
的延续。
该继续将求和任务(z
)设置为完成。它将恢复AddAsync
。
private static async Task AddAsync()
{
Console.WriteLine("***** Adding with Thread objects *****");
Console.WriteLine("ID of thread in Main(): {0}",
Thread.CurrentThread.ManagedThreadId);
AddParams ap = new AddParams(10, 10);
var z = Sum(ap);
await z;
Console.WriteLine("Other thread is done!"); // <-- second thread
}
现在,AddAsync
完成。但是,正如我上面所说,您只是忽略了AddAsync
返回的内容。您没有Wait
或await
或向其添加延续性...第二个线程没有别的事情要做,现在第二个线程消逝了。
注意:请注意,第二个线程来自ThreadPool
。您可以阅读Thread.CurrentThread.IsThreadPoolThread
检查自己。
我正在使用Retrofit来获取JSON答复。这是我实施的一部分-@GET("/api/report/list") Observable<Bills> listBill(@Query("employee_id") String employeeID); 而条例草案类是-public static class…
改造正在返回一个空的响应主体 - java我正在尝试使用Retrofit和Gson解析一些JSON。但是,我得到的响应机构是空的。当我尝试从对象中打印信息时,出现NullPointerException。我确保URL正确,并且我也确保POJO也正确。我正在使用jsonschema2pojo来帮助创建POJO类。这是我要解析的JSON{ "?xml": { "@versi…
每个文件合并后添加换行 - python我有很多类似以下内容的JSON文件:例如。1.json{"name": "one", "description": "testDescription...", "comment": ""} test.json{"name"…
Json到php,json_decode返回NULL - php我正在用PHP进行JSON解析器的一些API,用于存储有关遗产的信息。我在解析时遇到问题,因为它返回的是NULL值而不是数组或对象。简单的JSON代码可以很好地解析,但是可以这样:{"success":true,"totalCount":1,"data":[{"id":99694…
C#中的开关/案例情况下循环-如何? - c#我正在尝试使用c#扩展给定的代码,但是由于缺乏编程经验,我有点陷入困境。使用Visual Studio社区,我试图通过控制台读取CPU核心温度。该代码使用开关/外壳来查找传感器特定名称的外壳,例如CPU Core#1和温度。对于每个CPU内核。拥有64个核心,这是相当不错的选择。我试图实现一个foreach情况,但是它不起作用。我也不知道如何在这里设置for…