Blazor University (13)组件 — 多线程渲染

原文链接:https://blazor-university.com/components/multi-threaded-rendering/

多线程渲染

由于 Blazor Server 应用程序中可用的线程不止一个,因此完全有可能不同的组件可以让不同的线程在其上执行代码。

这在基于异步任务的操作中最常见。例如,向服务器发送 HTTP 请求的多个组件将收到单独的响应。每个单独的响应都将使用系统从可用线程池中为我们选择的任何线程来恢复调用方法。

我们观察这种行为的最简单方法是创建一些执行 await 的异步方法。对于此示例,我们将使用 OnInitializedAsync 生命周期[1]方法。

源代码[2]

为了演示这一点,我们首先需要创建一个新的 Blazor Server 应用程序。然后,在 /Shared 文件夹中,创建一个名为 SynchronousInitComponent 的组件。该组件会在执行 OnInitialized 时捕获当前线程的 Thread.ManagedThreadId。当我们的组件渲染时,该值将显示在页面上。

<p>Sync rendered by thread @IdOfRenderingThread</p>@code
{int IdOfRenderingThread;protected override void OnInitialized(){base.OnInitialized();IdOfRenderingThread =System.Threading.Thread.CurrentThread.ManagedThreadId;}
}
  • 第 5 行

    声明一个字段以保存对线程 ID 的引用。

  • 第 7 行

    OnInitialized 生命周期方法被重写。

  • 第 10 行

    当前线程的 ID 存储在 IdOfRenderingThread 中,因此可以渲染。

  • 第 1 行

    呈现在第 10 行捕获的线程的 ID。

最后,编辑 /Pages/Index.razor 页面以显示我们新组件的 5 个实例。

@page "/"<h1>Components with synchronous OnInitialized()</h1>
@for (int i = 0; i < 5; i++)
{<SynchronousInitComponent />
}

运行应用程序将为每个组件显示相同的线程 ID。显然,您的线程 ID 可能与我的不同。

Components with synchronous OnInitialized()
Sync rendered by thread 4
Sync rendered by thread 4
Sync rendered by thread 4
Sync rendered by thread 4
Sync rendered by thread 4

异步

接下来,我们将在名为 AsynchronousInitComponent/Shared 文件夹中创建另一个新组件。此组件将与 SynchronousInitComponent 相同,但会在等待 1 秒后在 OnInitializedAsync 中额外重新分配 IdOfRenderingThread 的值。

<p>Async rendered by thread @IdOfRenderingThread</p>@code
{int IdOfRenderingThread;protected override async Task OnInitializedAsync(){// Runs synchronously as there is no code in base.OnInitialized(),// so the same thread is usedawait base.OnInitializedAsync().ConfigureAwait(false);IdOfRenderingThread =System.Threading.Thread.CurrentThread.ManagedThreadId;// Awaiting will schedule a job for later, and we will be assigned// whichever worker thread is next availableawait Task.Delay(1000).ConfigureAwait(false);IdOfRenderingThread =System.Threading.Thread.CurrentThread.ManagedThreadId;}
}
  • 第 7 行

    OnInitializedAsync 生命周期方法被重写。

  • 第 12 行

    与同步组件一样,当前线程的 ·ManagedThreadId· 被分配给 ·IdOfRenderingThread·,因此它可以被组件渲染。(见说明)

  • 第 17 行

    在继续执行该方法之前,我们允许等待 1 秒。

  • 第 18 行

    IdOfRenderingThread 再次更新,显示在第 17 行等待 1 秒后重新渲染组件的线程 ID。

注意: 第 11 行的 await 将异步运行似乎是有道理的。事实上,它是同步运行的。这是因为基本方法什么都不做。异步代码(例如 Task.Delay)没有等待,因此同一线程继续执行。

我们还需要另一个页面来呈现这个新组件。在 /Pages 中使用以下标记创建一个名为 AsyncInitPage.razor 的新页面。

@page "/async-init"<h1>Components with asynchronous OnInitializedAsync()</h1>
@for (int i = 0; i < 5; i++)
{<AsynchronousInitComponent/>
}

运行应用程序并导航到第二个页面将产生与第一页非常相似的输出,其中每个组件都由单个线程渲染。

Components with asynchronous OnInitializedAsync()
Async rendered by thread 4
Async rendered by thread 4
Async rendered by thread 4
Async rendered by thread 4
Async rendered by thread 4

但是,1 秒后,每个组件的 OnInitializedAsync 方法中的 await Task.Delay(1000) 将在为浏览器渲染 HTML 之前完成并更新 IdOfRenderingThread。这一次,我们可以看到使用不同的线程来完成 OnInitializedAsync 方法。

Components with asynchronous OnInitializedAsync()
Async rendered by thread 7
Async rendered by thread 18
Async rendered by thread 10
Async rendered by thread 13
Async rendered by thread 11

ConfigureAwait(true) 会怎么样?

在我们的 await 上指定 ConfigureAwait(true) 并不能保证我们将看到我们的所有组件都渲染在启动 await 的同一线程上。指定ConfigureAwait(true) 仍将导致用于回调的线程混合。

Components with asynchronous OnInitializedAsync()
Async rendered by thread 11
Async rendered by thread 11
Async rendered by thread 9
Async rendered by thread 13
Async rendered by thread 13

即使 ConfigureAwait(true) 确实保证我们可以在同一个线程上继续,这仍然不能确保我们的 UI 仅由单个线程渲染。可能由于多种原因导致组件重新渲染,包括(但不限于)。

  • 来自 System.Threading.Timer 的回调

  • 由多个用户共享的 Singleton 实例上的另一个线程触发的事件

  • 来自我们通过 Web Socket 连接的另一台服务器的数据推送。

总结

在 Blazor Server 应用程序中,没有单个 UI 线程。当需要渲染工作时,可以使用任何可用的线程。

此外,如果任何方法在执行异步操作的代码上使用了 await,则分配用于继续处理该方法的线程很可能与启动它的线程不同。

在 Blazor WebAssembly 应用程序(只有一个线程)中没有线程问题,但在服务器端应用程序中,当跨多个组件使用非线程安全依赖项时,这可能会导致问题。

此问题将在有关 OwningComponentBase[3] 的部分中解决。

线程安全的使用 InvokeAsync

在我们的代码被非 UI 事件调用的情况下(例如多线程渲染[4]中介绍的那些),如果我们打算操纵状态,我们通常需要实现某种类型的线程锁定/同步。

回顾: 非 UI 事件包括:

  • 来自 System.Threading.Timer 的回调

  • 由多个用户共享的 Singleton 实例上的另一个线程触发的事件

  • 来自我们通过 Web Socket 连接的另一台服务器的数据推送。

为了避免手动编写线程安全代码,编写 WPF 应用程序的人可能会使用 Dispatcher.Invoke 来确保 UI 线程执行代码,而 WinForms 开发人员可能会使用窗体的 Invoke 方法。以这种方式调用的任何代码始终由特定线程(UI 线程)执行,这避免了使用线程同步代码。

StateHasChanged[5] 框架方法,用于告诉 Blazor 重新渲染我们的组件,不允许多个线程同时访问渲染进程。如果辅助线程调用 StateHasChanged,则会引发异常。

System.InvalidOperationException:当前线程未与 Dispatcher 关联。

在 Blazor Server 应用程序中,有一个与每个连接(每个浏览器选项卡)关联的调度程序。当我们使用 InvokeAsync 时,我们正在通过此调度程序执行操作(就像 WPF Dispatcher.Invoke 或 WinForms Control.Invoke)。

在前面介绍的场景之一中调用 StateHasChanged 时(从线程执行代码等),有必要通过 InvokeAsync() 方法调用它。InvokeAsync 将串行化操作,因此将避免 StateHasChanged 引发异常。

尽管方法将由任意数量的不同线程执行,但在任何给定时刻只有一个线程将访问组件,从而无需围绕共享状态编写线程锁定/同步代码。

InvokeAsync 示例

源代码[6]

为了演示直接从线程执行组件方法与通过 InvokeAsync 执行之间的行为差异,我们将创建一个服务器端应用程序,该应用程序将展示多个并发线程如何破坏共享状态。

创建新的 Blazor 服务器端应用程序后,添加一个静态类,该类将存储一个整数值,多个组件/线程可以访问该值。

public static class CounterState
{public static int Value { get; set; }
}

显示状态

我们将在组件中显示此状态的值,并每秒检查两次该值。为此,我们将在 /Shared 文件夹中创建一个名为 ShowCounterValue.razor 的组件。

@implements IDisposable
<div>Counter value is: @CounterState.Value at @DateTime.UtcNow.ToString("HH:mm:ss")
</div>@code
{private System.Threading.Timer Timer;protected override void OnInitialized(){base.OnInitialized();Timer = new System.Threading.Timer(_ =>{InvokeAsync(StateHasChanged);}, null, 500, 500);}void IDisposable.Dispose(){Timer?.Dispose();Timer = null;}
}
  • 第 1 行

    声明我们的组件实现了 IDisposable

  • 第 3 行

    显示 CounterState.Value 的当前值以及当前时间。

  • 第 13 行

    当组件初始化时,会创建一个 System.Threading.Timer,它将每 500 毫秒执行一次 StateHasChanged。它通过 InvokeAsync 调用,以防止 Blazor 抛出异常,告诉我们我们正在从线程调用 StateHasChanged

  • 第 21 行

    当组件被释放时释放定时器。

注意:如果计时器没有被释放,那么它将在用户会话的整个生命周期内保持活动状态。如果计时器保持活动状态,则不会对组件进行垃圾回收,因为计时器回调通过其 InvokeAsyncStateHasChanged 方法持有对组件的隐式引用。

修改状态

我们现在将创建一个组件,它将递增 CounterState.Value 字段。每个组件将在一个线程中执行一个循环并更新 Value 1000 次。然后我们可以在我们的主页上拥有这个组件的多个实例,以确保多个线程正在更新状态。

注意:这个组件将被传递一个 System.Threading.WaitHandle。组件的线程会被挂起,直到主页面触发这个 WaitHandle,触发所有线程同时开始循环。

/Shared 文件夹中,创建一个名为 IncrementCounter.razor 的新文件。我们将从一些文本开始,以显示该组件存在于页面上,一个接受所需 WaitHandle 的参数,以及一个指示我们是否要使用 InvokeAsync 来增加值的参数。

<div>Incrementing...
</div>
@code
{[Parameter]public bool ShouldUseInvokeAsync { get; set; }[Parameter]public System.Threading.WaitHandle Trigger { get; set; }// More to come
}

为了增加 CounterState.Value,我们将在 OnInitialized 生命周期方法中创建一个线程。我们将立即启动线程,但线程中的第一条指令将暂停自身,直到触发 WaitHandle

protected override void OnInitialized()
{var thread = new System.Threading.Thread(_ =>{Trigger.WaitOne();for (int i = 0; i < 1000; i++){if (!ShouldUseInvokeAsync){CounterState.Value++;}else{InvokeAsync(() => CounterState.Value++);}}});thread.Start();
}
  • 第 1 行

    重写 OnInitialized

  • 第 3 行

    创建一个线程来修改共享状态。

  • 第 5 行

    线程应该做的第一件事是等待父页面的 WaitHandle 被触发。

  • 第 6 行

    对共享状态执行 1000 次更改。

  • 第 8 行

    检查我们是否应该使用 InvokeAsync

  • 第 10 行

    直接在当前线程更改共享状态。

  • 第 14 行

    在更改共享状态时使用 InvokeAsync 确保顺序访问。

  • 第 19 行

    启动线程,以便它可以等待被触发。

使用组件演示状态冲突

最后一步是在我们的 Index 页面中创建一些标记,这将创建其中的 5 个组件。

为了使应用程序更有趣,我们还将

  1. 有一个复选框,允许用户指定是否希望通过 InvokeAsync 执行状态更改。

  2. 允许用户重置应用程序的状态并通过单击按钮重试。

如果用户想要使用 InvokeAsync,我们需要在 Index 页面中显示一些组件状态来指示我们当前正在运行测试,以及一个 System.Threading.ManualReset 对象,我们可以将其传递给我们的 IncrementCounter 以触发对其的处理线程。

private bool IsWorking;
private bool UseInvokeAsync;
private System.Threading.ManualResetEvent Trigger = new System.Threading.ManualResetEvent(false);

对于标记,我们需要使用双向绑定[7]IsWorking 字段绑定到 HTML <input> 元素。

<div><input type="checkbox" @bind=UseInvokeAsync id="UseInvokeAsyncCheckbox" /><label for="UseInvokeAsyncCheckbox">Use InvokeAsync</label>
</div>

随后是一个按钮,用户可以单击以开始测试运行。在测试运行期间应禁用该按钮。

<div><button @onclick=Start disabled=@IsWorking>Start</button>
</div>

我们还想使用 ShowCounterValue 让我们的用户了解 CounterState.Value 的当前值。

<ShowCounterValue />

最后,对于标记,我们将要创建 5 个 IncrementCounter 组件实例。这些组件只会在测试运行开始后创建,并在运行完成后被丢弃。为此,我们仅在 IsWorkingtrue 时才渲染它们。

@if (IsWorking)
{for (int i = 0; i < 5; i++){<IncrementCounter Trigger=@Trigger ShouldUseInvokeAsync=@UseInvokeAsync />}
}
  • 第 1 行

    仅当 IsWorkingtrue 时才渲染组件,表示正在进行测试运行。

  • 第 3 行

    用一个循环来创建我们的 IncrementCounter 组件的 5 个实例。

  • 第 5 行

    创建一个 IncrementCounter 实例,传入我们的 ManualResetEvent (Trigger) 和一个布尔值,指示用户是否在 UI 中单击了 Use InvokeAsync

我们现在必须编写的唯一代码是 Start 方法。这将简单地重置状态,将 IsWorking 设置为 true,触发我们的组件开始递增,然后将 IsWorking 设置为 false

Index 页面现在应该如下所示:

@page "/"
<div><input type="checkbox" @bind=UseInvokeAsync id="UseInvokeAsyncCheckbox" /><label for="UseInvokeAsyncCheckbox">Use InvokeAsync</label>
</div>
<div><button @onclick=Start disabled=@IsWorking>Start</button>
</div>
<ShowCounterValue />
@if (IsWorking)
{for (int i = 0; i < 5; i++){<IncrementCounter Trigger=@Trigger ShouldUseInvokeAsync=@UseInvokeAsync />}
}@code
{private bool IsWorking;private bool UseInvokeAsync;private System.Threading.ManualResetEvent Trigger = new System.Threading.ManualResetEvent(false);private async Task Start(){CounterState.Value = 0;IsWorking = true;StateHasChanged();await Task.Delay(500);Trigger.Set();await Task.Delay(1000);IsWorking = false;Trigger.Reset();}
}
  • 第 3 行

    将 HTML <input> 绑定到 UseInvokeAsync

  • 第 7 行

    单击时执行 Start 方法的按钮。如果 IsWorking 为真,则该按钮被禁用。

  • 第 9 行

    使用 CounterState.Value 的当前值更新 UI。

  • 第 14 行

    渲染一些 IncrementCounter 组件,但前提是 IsWorking 为真。

  • 第 26-28 行

    将共享状态重置为零。

  • 第 30 行

    等待 500 毫秒,让所有组件中的线程有机会启动。

  • 第 31 行

    触发我们的 ManualResetEvent (Trigger),以便所有 IncrementCounter 组件的线程可以同时恢复并开始修改共享状态。

  • 第 33-35 行

    等待 1 秒以使测试完成,然后重置 IsWorkingManualResetEvent,以便它们准备好进行另一次休息运行。

运行示例应用程序

在没有 InvokeAsync 的情况下运行

运行应用程序,并在第一个测试中取消选中 Use InvokeAsync 复选框。然后单击开始按钮,您将看到类似下图的内容。

c693d8c5ac971aabe8a66757d59155b5.png

1 秒后出现如下图所示的屏幕。请注意,在我的情况下,最终的 CounterState.Value 仅为 1202。考虑到有 5 个组件,每个组件在 1000 次迭代循环中将值递增 1,理想情况下,我们应该将 5000 视为最终值。

4475ac3049f4de4d43981ddb91533dca.png

使用 InvokeAsync 运行

接下来,勾选复选框并再次单击“开始”按钮。

eb66efdafa8b0d965bbb39f2df0bd5d0.png

一秒钟后,其余运行将完成,我们看到了更理想的结果。

7c19f3e724374af9cffd0e46e54ab8f9.png

总结

在处理 UI 触发事件(按钮点击、导航事件等)时,我们不需要对线程安全做任何特殊考虑。Blazor 将为我们管理这一点,并确保任何时候只有一个线程在执行组件代码。

当非 UI 事件触发我们的代码时,然后在服务器端 Blazor 应用程序中,此代码将在非同步线程中触发。无法调用 StateHasChanged,并且由于线程竞争条件[8],对任何共享状态的访问都容易损坏。

注意: Blazor WebAssembly 应用是单线程的,因此不必考虑线程安全。

ComponentBase 类上引入的 Blazor InvokeAsync 将通过同步每个用户连接的线程执行来确保不会发生争用条件。

Blazor 能够通过在用户连接到 Blazor 服务器端应用程序时创建的 Dispatcher 执行代码来确保在任何给定时间只有一个线程正在执行代码。

要考虑的一个可能的复杂情况是 Blazor Dispatcher 不会确保在执行下一个分派的代码之前运行整个代码段。如果分派的所有动作都是同步代码,那么就是这种情况,但是,如果任何分派的操作是异步的,那么该线程将在对某些异步代码(例如 Task.Delay 或 HTTP 请求)执行 await 时放弃其执行时间。

这意味着尽管 Blazor 的 InvokeAsync 可以保证线程在执行中同步,但这并不意味着我们在使用异步资源(例如对 Entity Framework Core 的查询)时可以不使用线程安全代码。

以下由两个单独线程执行的代码将导致多个线程同时尝试使用相同的 Entity Framework Core DbContext,即使通过 InvokeAsync 调用也是如此。

IsLoading = true;
StateHasChanged();Person[] people = await ApplicationDbContext.Person.ToArrayAsync();
IsLoading = false;

这是因为 await 语句放弃执行并允许 Blazor 让不同的逻辑代码块执行。

65e16a174e42dc9e4ee9c786d89b9a63.png

在上图中,在步骤 2 中,两个单独的线程已指示 ApplicationDbContext 执行异步操作。因为 Entity Framework Core 不支持线程重入,它会抛出一个异常,告诉我们在任何给定时间只有一个线程可以访问 DbContext

因此,尽管在通过 InvokeAsync 执行的同步代码中共享状态是安全的,但请记住,一旦您的代码引入了 await,它就允许其他线程有机会介入,直到 await 完成。

参考资料

[2]

源代码: https://github.com/mrpmorris/blazor-university/tree/master/src/Components/MultithreadedRendering

[6]

源代码: https://github.com/mrpmorris/blazor-university/tree/master/src/Components/UsingInvokeAsync

[8]

线程竞争条件: https://en.wikipedia.org/wiki/Race_condition

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/288662.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

sql 之like 和通配符%,_(mysql)

(&#xff61;ŏ_ŏ) like模糊查询&#xff0c;啥叫模糊查询&#xff1f; 例如&#xff1a;我们一个数据库里面存在在一个人叫做李二三四。我们忘记了他的名字&#xff0c;只记得他的姓名&#xff0c;那么我们就可以使用like加上通配符来查询出我们所要的结果&#xff1b;话说…

php邮件代码c语言,C语言实现邮件发送功能(SMTP)源码

【实例简介】C 语言编写的邮件发送器是SMTP协议的源代码和EXE执行程序均在里面使用VS2013开发环境生成&#xff0c;填写对应参数即可成功进行邮件发送&#xff0c;不用配置邮件服务器&#xff0c;只需一个支持SMTP协议的邮箱账号密码即可【实例截图】【核心代码】#include #inc…

【线性筛】【质因数分解】【约数个数定理】hdu6069 Counting Divisors

d(x)表示x的约数个数&#xff0c;让你求&#xff08;l,r<10^12,r-l<10^6,k<10^7&#xff09; #include<cstdio> using namespace std; #define MOD 998244353ll #define MAXP 1000100 typedef long long ll; ll x,y; int T,K; bool isNotPrime[MAXP10]; int num…

C#/.Net 不要再使用Aspose和iTextSharp啦!QuestPDF操作生成PDF更快更高效!

QuestPDFQuestPDF是一个开源的工具库&#xff0c;可以在.NET或者.Net Core中生成pdf文档它提供了一个布局引擎&#xff0c;设计时考虑到了完整的分页支持以及灵活性要求&#xff01;比市面上常见的Aspose和iTextSharp好用太多了&#xff01;GitHub地址安装Install-Package Ques…

C#创建桌面快捷方式

1、添加引用Windows Script Host Object Model,并引用命名空间using IWshRuntimeLibrary; 2、代码 using System; using IWshRuntimeLibrary; using System.Windows.Forms;namespace WindowsFormsApplication1 {public partial class Form1 : Form{public Form1(){InitializeC…

Java报表工具FineReport导出EXCEL的四种API

在实际的应用中会经常需要将数据导出成excel&#xff0c;导出的方式除原样导出还有分页导出、分页分sheet导出和大数据量导出。对于excel 2003版&#xff0c;由于限制了每个sheet的最大行数和列数&#xff0c;大数据量导出时会默认时分多个sheet&#xff0c;而excel2007不会出现…

sql in 用法(mysql)

我们先看一个如下数据库表&#xff1a; 我们如果想查询这张表里面age为11和1的人该怎么办呢&#xff1f; 那么我们的 in 操作符就起作用了&#xff1a; SELECT * FROM table1 WHERE age1 IN(11,1); 查询来自表哥table1的数据&#xff0c;条件为age1 在(11,1)这两个数之中…

matlab的循环语句裁图,[MATLAB图像处理] 多幅图片处理的循环语句

多幅图片处理的循环语句 小弟毕业设计关于视频去雾处理&#xff0c;将视频截取为图片后对每张图片进行处理&#xff0c;其中需要用到循环语句来减少工作量&#xff0c;但自己对循环语句不怎么会&#xff0c;希望哪位大大能够给予帮助&#xff0c;谢谢了程序如下block_size 15;…

开源社·读书播客第一期:《大教堂与集市》

点击蓝字&#xff0c;关注我们&#xff5c;作者&#xff1a;许银&#xff5c;编辑&#xff1a;Corrie&#xff5c;设计&#xff1a;朱亿钦Hi&#xff0c;各位开源爱好者&#xff0c;在世界读书日这个特殊的日子里&#xff0c;我们开源社踩着潮流来了&#xff0c;近一年来&#…

LeetCode:Count Primes

Problem: Description: Count the number of prime numbers less than a non-negative number, n. Credits:Special thanks to mithmatt for adding this problem and creating all test cases. Solution:采用的为埃拉托斯特尼筛法 算法描述&#xff1a;&#xff08;来自百度…

华为交换机查看端口流量_华为交换机限速及流量统计配置

配置交换机限速时&#xff0c;cir和cbs的关系是什么配置交换机限速时&#xff0c;cir和cbs的关系如下&#xff1a;cbs要大于报文的最大长度。在连续流量的情况下对于cbs没有特殊的要求&#xff0c;保证平均速率是cir的速率。在突发流量需要保证的情况下&#xff0c;如果cbs换算…

利用Matlab拟合时序植被生长季曲线,并求解物候参数

研究表明,一元六次曲线比一元二次曲线更好地模拟植被生长季的整个生长周期,表征植被的生长特性。matlab软件功能强大,在遥感模型研究方面有很大的优势特点,对数学模型模拟轻而易举,也可直接对栅格数据进行运算。因此本文采用matlab软件,采用一元6次曲线来拟合植被生长季曲…

sql between...and 用法(mysql)

首先我们看如下数据库表&#xff1a; 如果我们想查询如上表中的age1的值在0到5的数据该如何查询&#xff1f; 那么在这里我们用到我们的sql操作符between…adn。 SELECT * FROM table1 WHERE age1 BETWEEN 0 AND 5 其中between后面的0 and 5 意思就是 查询寻数据&#xf…

如何在JavaScript中运行.NET Core代码

前言在.NET Core中运行JavaScript代码&#xff0c;目前已经有很多实现方案。但是&#xff0c;如果你希望在纯JavaScript环境中运行.NET Core代码呢&#xff1f;那么&#xff0c;DotNetJS可能对你有所帮助。DotNetJSDotNetJS可以将C#项目编译为与任何环境兼容的单文件JavaScript…

sql 之as(Aliases)别名(mysql)

使用别名主要原因是因为如果在很多表的时候&#xff0c;我们的表名可能会混淆&#xff0c;导致语句不清晰&#xff0c;那么我们使用别名就可以让我们操作数据库表的时候更加清晰明了&#xff0c;那么我们假设有如下表&#xff1a; 那么我们查找我们age1的值为11的数据&#…

sqlserver建表语句_重新认识MySQL中的COUNT语句

在数据库的增删改查操作中&#xff0c;使用最频繁的就是查询操作。而在所有查询操作中&#xff0c;统计数量操作更是经常被用到。关于数据库中行数统计&#xff0c;无论是MySQL还是Oracle亦或者是SqlServer&#xff0c;都有一个函数可以使用&#xff0c;那就是COUNT。而对于COU…

sql INNER JOIN 取得两个表中存在连接匹配关系的记录(mysql)

首先&#xff1a;JOIN 通常与 ON 关键字搭配使用 其次我们来看我们的两个表格&#xff1a; table1: table2: 在这里&#xff0c;INNER JOIN&#xff08;内连接,或等值连接&#xff09;&#xff1a;取得两个表中存在连接匹配关系的记录。 例如我要取到table1和table2之…

【Linux】IPC-消息队列

问题 消息队列id 和键值KEY区别&#xff1f; 首先要注意一个概念&#xff1a;IPC结构都是内核的结构。也就是说IPC结构由内核维护&#xff0c;对于每个进程都是公共的&#xff0c;不属于某个特定进程。只有这样&#xff0c;IPC结构才能支持它们“进程间通信”的功能。 有两个东…

Blazor University (14)渲染树

原文链接&#xff1a;https://blazor-university.com/components/render-trees/渲染树当浏览器呈现内容时&#xff0c;它不仅绘制 HTML 中定义的元素&#xff0c;还必须根据页面大小&#xff08;元素流&#xff09;计算绘制它们的位置。例如&#xff0c;以下 Bootstrap HTML 将…