{"id":95455,"date":"2017-02-16T07:00:00","date_gmt":"2017-02-16T22:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/?p=95455"},"modified":"2019-03-13T01:06:19","modified_gmt":"2019-03-13T08:06:19","slug":"20170216-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20170216-00\/?p=95455","title":{"rendered":"How do I fix the problem of a long-running task running on the thread pool persistent thread?"},"content":{"rendered":"<p>Last time, we diagnosed a problem caused by a long-running task on the thread pool persistent thread, which prevented other tasks which target the persistent thread from running. But what motivated the developers to write code that put a long-running task on the persistent thread in the first place? <\/p>\n<p>My theory is that the developers saw this sentence in the documentation for <code>Reg&shy;Notify&shy;Change&shy;Key&shy;Value<\/code>: <\/p>\n<blockquote CLASS=\"q\"><p>With the exception of <b>Reg&shy;Notify&shy;Change&shy;Key&shy;Value<\/b> calls with <b>REG_NOTIFY_THREAD_AGNOSTIC<\/b> set, this function must be called on persistent threads. <\/p><\/blockquote>\n<p>The reasoning was probably, &#8220;Well, the documentation says that this must be called on a persistent thread, so we have to schedule it with the <code>WT_EXECUTE&shy;IN&shy;PERSISTEN&shy;THREAD<\/code> flag.&#8221; <\/p>\n<p>That&#8217;s not what the documentation is trying to say, but I can&#8217;t fault them for misinterpreting it that way. <\/p>\n<p>What the documentation is trying to say is, &#8220;If you know what&#8217;s good for you, you will call this function on a thread that will not exit until you close the registry key. If the thread exits prematurely, then the notification will stop working (in a specific way described below, though you would be best to just avoid the problem entirely).&#8221; <\/p>\n<p>The documentation is using the word &#8220;persistent thread&#8221; here in a generic sense, meaning any thread that does not exit (until the thing you care about is over). It doesn&#8217;t have to run on the thread pool&#8217;s persistent thread; any persistent thread will do. <\/p>\n<p>The callback function registered by the application does not close the registry key handle until it has finished with the change notification, so it&#8217;s fine to run this code on any thread; it doesn&#8217;t have to run on the thread pool&#8217;s persistent thread. <\/p>\n<p>Therefore, one fix for the problem is to remove the <code>WT_EXECUTE&shy;IN&shy;PERSISTEN&shy;THREAD<\/code> flag. This runs the work item on a thread pool thread with no special attributes. You still want the <code>WT_EXECUTE&shy;LONG&shy;FUNCTION<\/code> flag because the work item runs long: It runs indefinitely until the monitoring is stopped. <\/p>\n<p>However, running a long function with indefinite lifetime isn&#8217;t really the thread pool&#8217;s bread and butter. The thread pool is really for running large numbers of short items. After all, scheduling a work item on the thread pool that runs indefinitely isn&#8217;t really all that different from running a dedicated thread for the work item. The purpose of the thread pool is to amortize the cost of starting up a thread over many work items. Otherwise, the system will be spending more time starting up threads than it does actually performing work. <\/p>\n<p>This benefit doesn&#8217;t really help work items with indefinite running time. From a percentage standpoint, the added cost of starting up a new thread is not significant if the thread is going to be running for minutes. <\/p>\n<p>But if you look at the work item that the customer was scheduling, it&#8217;s not really doing work most of the time anyway. It spends most of its time waiting! Next time, we&#8217;ll look at another way of designing the code so that instead of burning a thread for each active monitoring request, it pools the requests. <\/p>\n<p>In other words, we&#8217;re going to use the thread pool as a thread pool. Tune in next time for the exciting conclusion. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hey, you, get off of my thread.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-95455","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Hey, you, get off of my thread.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/95455","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=95455"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/95455\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=95455"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=95455"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=95455"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}