PyQt5桌面应用开发(12):QFile与线程安全

news2024/12/24 22:18:21

本文目录

  • PyQt5桌面应用系列
  • segment fault
  • gdb backtrace
  • open & read
  • QFile
  • 总结

PyQt5桌面应用系列

  • PyQt5桌面应用开发(1):需求分析
  • PyQt5桌面应用开发(2):事件循环
  • PyQt5桌面应用开发(3):并行设计
  • PyQt5桌面应用开发(4):界面设计
  • PyQt5桌面应用开发(5):对话框
  • PyQt5桌面应用开发(6):文件对话框
  • PyQt5桌面应用开发(7):文本编辑+语法高亮与行号
  • PyQt5桌面应用开发(8):从QInputDialog转进到函数参数传递
  • PyQt5桌面应用开发(9):经典布局QMainWindow
  • PyQt5桌面应用开发(10):界面布局基本支持
  • PyQt5桌面应用开发(11):摸鱼也要讲基本法,两个字,16
  • PyQt5桌面应用开发(12):QFile与线程安全

segment fault

一个可爱的PyQt5程序,但有有点瑕疵……

editor

点击左边的文件树中的文件时,就会打开文件,读入内容并设置到中间的代码编辑器控件中。程序启动时,自动读入本文对应的python源程序。

self.editor.setPlainText(read_file("segfaults_pyqt5.py"))

第一次读入时,一切正常,文件正常载入和显示。但是当点击文件读入时间触发后,程序会自动退出,显示段错误 (核心已转储)

这究竟是怎么回事呢?真相只有一个,我们再来完整看看源代码。

import time
from PyQt5.QtCore import Qt, QDir, QModelIndex, QFile, QIODevice
from PyQt5.QtGui import QIcon, QTextDocument
from PyQt5.QtWidgets import QApplication, QTreeWidgetItem, QToolBar, QStatusBar, QStyle, QFormLayout, QLineEdit
from PyQt5.QtWidgets import QDockWidget
from PyQt5.QtWidgets import QMainWindow, QWidget, QMenuBar, QTreeWidget

from code_editor import QCodeEditor
from py_syntax_highlighter import PythonHighlighter

def read_file(filename) -> str:
    return open(filename, 'r', encoding="utf-8").read()

class MyMainWindow(QMainWindow):
    def __init__(self):
        super(MyMainWindow, self).__init__()
        self.setWindowIcon(QIcon('icon.png'))
        self.setWindowTitle('PyQt5 Editor')
        self.editor = QCodeEditor(display_line_numbers=True,
                                  highlight_current_line=True,
                                  syntax_high_lighter=PythonHighlighter)
        self.setCentralWidget(self.editor)

        # 第一次读入文件:OK
        self.editor.setPlainText(read_file("segfaults_pyqt5.py"))
        self._set_menubar()
        self._set_toolbar()
        self._set_statusbar()
        self._set_left_dock()
        self._set_right_dock()

    def _set_menubar(self: QMainWindow):
        mb = QMenuBar(parent=self)

        file_menu = mb.addMenu('&File')
        for a in ['&New', '&Open', '&Save', '&Save as']:
            file_menu.addAction(QIcon("icon.png"), a, self.close)
        file_menu.addSeparator()
        file_menu.addAction(self.style().standardIcon(QStyle.SP_DialogCloseButton), '&Quit', self.close)

        self.setMenuBar(mb)

    def _set_toolbar(self: QMainWindow):
        tb = QToolBar(self)

        for a in ['New', 'Open', 'Save', 'Save as']:
            tb.addAction(QIcon("icon.png"), a, self.close)
        tb.addSeparator()
        tb.addAction(self.style().standardIcon(QStyle.SP_DialogCloseButton), "Close", self.close)
        tb.setToolButtonStyle(Qt.ToolButtonFollowStyle)
        # tb.setMovable(False)

        self.addToolBar(tb)

    def _set_statusbar(self: QMainWindow):
        sb = QStatusBar(self)
        sb.showMessage("Hello!")

        self.setStatusBar(sb)

    def _set_left_dock(self: QMainWindow):
        dock = QDockWidget('Project structure', self)
        tv = QTreeWidget(dock)
        tv.setColumnCount(4)
        # tv.setHeaderHidden(True)
        tv.setHeaderLabels(['', '', 'name', 'path'])

        root = self._get_file_tree(".", tv)

        root.setExpanded(True)

        tv.addTopLevelItem(root)

        dock.setWidget(tv)

        dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)
        dock.setFeatures(QDockWidget.DockWidgetMovable)
        self.addDockWidget(Qt.LeftDockWidgetArea, dock)

        self.tv = tv
        self.tv.clicked.connect(self.load_item)

    def load_item(self, index: QModelIndex):
        item = self.tv.itemFromIndex(index)
        fn = item.text(3)
        if QDir(fn).exists():
            return

        if fn.endswith(".py"):
            try:
                #####################################################
                # 触发点击时间后,调用到这里出问题
                # 段错误:到底是什么原因呢?
                self.editor.setPlainText(read_file(fn))   
                #####################################################             
                doc: QTextDocument = self.editor.document()
                self.statusBar().showMessage(f"Open file {fn}, length: {doc.characterCount()}.")
            except Exception as e:
                print(e)

    def _set_right_dock(self: QMainWindow):

        dock = QDockWidget('File information', self)
        info = QWidget(dock)
        layout = QFormLayout(info)

        self.line_edit = QLineEdit(f"{self.editor.document().blockCount()}")
        self.line_edit.setEnabled(False)
        self.character_edit = QLineEdit(f"{self.editor.document().characterCount()}")
        self.character_edit.setEnabled(False)
        layout.addRow("Block count", self.line_edit)
        layout.addRow("Character count", self.character_edit)

        info.setLayout(layout)

        dock.setWidget(info)
        dock.setFeatures(QDockWidget.DockWidgetMovable)
        self.addDockWidget(Qt.RightDockWidgetArea, dock)

        doc: QTextDocument = self.editor.document()
        doc.blockCountChanged.connect(lambda count: self.line_edit.setText(f"{count}"))
        doc.contentsChanged.connect(lambda: self.character_edit.setText(f"{doc.characterCount()}"))

    def _get_file_tree(self, root_dir, parent) -> QTreeWidgetItem:

        d = QDir(QDir(root_dir).absolutePath())
        item = QTreeWidgetItem(parent)
        item.setIcon(1, self.style().standardIcon(QStyle.SP_DirIcon))
        item.setText(2, d.dirName())
        item.setText(3, d.canonicalPath())

        for f in d.entryInfoList(QDir.NoDot | QDir.NoDotDot | QDir.Dirs):
            if f.isDir():
                self._get_file_tree(f.canonicalFilePath(), item)

        for f in d.entryInfoList(["*.py"], QDir.NoDot | QDir.NoDotDot | QDir.Files):
            fi = QTreeWidgetItem(item)
            fi.setText(2, f.fileName())
            fi.setText(3, f.canonicalFilePath())
            if f.fileName().endswith("py"):
                fi.setIcon(1, QIcon("icon.png"))

        return item


if __name__ == '__main__':
    import sys

    app = QApplication(sys.argv)
    app.setStyleSheet("QPlainTextEdit {background-color: cyan} ")
    main_window = MyMainWindow()

    main_window.setMinimumSize(1440, 768)
    main_window.show()
    sys.exit(app.exec_())

gdb backtrace

这个时候,就可以用上调试工具gdb了。

gdb -ex r -ex "thread apply all backtrace" --args python segfaults_pyqt5.py

点击图形界面中左边的文件,引发段错误,打印出的跟踪信息如下,一共三个线程QDBusConnection,QXcbEventQueue和python。在python线程的#9行,我们可以看到是meth_QPlainTextEdit_setPlainText (),出问题的调用看来就是这里。

Thread 3 (Thread 0x7fffe8969640 (LWP 60056) "QDBusConnection"):
#0  0x00007ffff7d5bd7f in __GI___poll (fds=0x7fffe4004ed0, nfds=3, timeout=-1) at ../sysdeps/unix/sysv/linux/poll.c:29
#1  0x00007ffff6146666 in ?? () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
#2  0x00007ffff60ef3e3 in g_main_context_iteration () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
#3  0x00007ffff66f91cc in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/Qt5/lib/libQt5Core.so.5
#4  0x00007ffff669c21a in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/Qt5/lib/libQt5Core.so.5
#5  0x00007ffff64b2844 in QThread::exec() () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/Qt5/lib/libQt5Core.so.5
#6  0x00007ffff1215fd5 in QDBusConnectionManager::run() () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/Qt5/plugins/platforms/../../lib/libQt5DBus.so.5
#7  0x00007ffff64b3b35 in QThreadPrivate::start(void*) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/Qt5/lib/libQt5Core.so.5
#8  0x00007ffff7cd7b43 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#9  0x00007ffff7d69a00 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81

Thread 2 (Thread 0x7ffff10c1640 (LWP 60055) "QXcbEventQueue"):
#0  0x00007ffff7d5bd7f in __GI___poll (fds=0x7ffff10c0ca8, nfds=1, timeout=-1) at ../sysdeps/unix/sysv/linux/poll.c:29
#1  0x00007ffff5be27e2 in ?? () from /lib/x86_64-linux-gnu/libxcb.so.1
#2  0x00007ffff5be422c in xcb_wait_for_event () from /lib/x86_64-linux-gnu/libxcb.so.1
#3  0x00007ffff16647b0 in QXcbEventQueue::run() () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/Qt5/plugins/platforms/../../lib/libQt5XcbQpa.so.5
#4  0x00007ffff64b3b35 in QThreadPrivate::start(void*) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/Qt5/lib/libQt5Core.so.5
#5  0x00007ffff7cd7b43 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#6  0x00007ffff7d69a00 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81


Thread 1 (Thread 0x7ffff7c42000 (LWP 60046) "python"):
#0  0x00000000004fd33c in _PyEval_EvalFrameDefault ()
#1  0x0000000000531823 in _PyFunction_Vectorcall ()
#2  0x00007ffff6f630b0 in PyQtSlot::call(_object*, _object*) const () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/QtCore.abi3.so
#3  0x00007ffff6f63558 in PyQtSlot::invoke(void**, _object*, void*, bool) const () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/QtCore.abi3.so
#4  0x00007ffff6f6384e in PyQtSlotProxy::unislot(void**) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/QtCore.abi3.so
#5  0x00007ffff6f64317 in PyQtSlotProxy::qt_metacall(QMetaObject::Call, int, void**) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/QtCore.abi3.so
#6  0x00007ffff66d5f97 in void doActivate<false>(QObject*, int, void**) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/Qt5/lib/libQt5Core.so.5
#7  0x00007ffff3287cec in QTextDocumentPrivate::finishEdit() () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/Qt5/lib/libQt5Gui.so.5
#8  0x00007ffff21364fb in QWidgetTextControlPrivate::setContent(Qt::TextFormat, QString const&, QTextDocument*) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/Qt5/lib/libQt5Widgets.so.5
#9  0x00007ffff29f78ff in meth_QPlainTextEdit_setPlainText () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/QtWidgets.abi3.so
#10 0x000000000051ad90 in ?? ()
#11 0x00000000004e75dc in _PyObject_MakeTpCall ()
#12 0x00000000004fb152 in _PyEval_EvalFrameDefault ()
#13 0x000000000055e097 in ?? ()
#14 0x000000000055c852 in ?? ()
#15 0x00007ffff6f630b0 in PyQtSlot::call(_object*, _object*) const () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/QtCore.abi3.so
#16 0x00007ffff6f63558 in PyQtSlot::invoke(void**, _object*, void*, bool) const () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/QtCore.abi3.so
#17 0x00007ffff6f6384e in PyQtSlotProxy::unislot(void**) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/QtCore.abi3.so
#18 0x00007ffff6f64317 in PyQtSlotProxy::qt_metacall(QMetaObject::Call, int, void**) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/QtCore.abi3.so
#19 0x00007ffff66d5f97 in void doActivate<false>(QObject*, int, void**) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/Qt5/lib/libQt5Core.so.5
#20 0x00007ffff21a2105 in QAbstractItemView::clicked(QModelIndex const&) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/Qt5/lib/libQt5Widgets.so.5
#21 0x00007ffff21a5013 in QAbstractItemView::mouseReleaseEvent(QMouseEvent*) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/Qt5/lib/libQt5Widgets.so.5
#22 0x00007ffff2215be3 in QTreeView::mouseReleaseEvent(QMouseEvent*) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/Qt5/lib/libQt5Widgets.so.5
#23 0x00007ffff2b637db in sipQTreeWidget::mouseReleaseEvent(QMouseEvent*) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/QtWidgets.abi3.so
#24 0x00007ffff1fa1740 in QWidget::event(QEvent*) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/Qt5/lib/libQt5Widgets.so.5
#25 0x00007ffff204a21e in QFrame::event(QEvent*) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/Qt5/lib/libQt5Widgets.so.5
#26 0x00007ffff21ae2bc in QAbstractItemView::viewportEvent(QEvent*) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/Qt5/lib/libQt5Widgets.so.5
#27 0x00007ffff221564c in QTreeView::viewportEvent(QEvent*) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/Qt5/lib/libQt5Widgets.so.5
#28 0x00007ffff2b6614b in sipQTreeWidget::viewportEvent(QEvent*) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/QtWidgets.abi3.so
#29 0x00007ffff669d5a0 in QCoreApplicationPrivate::sendThroughObjectEventFilters(QObject*, QEvent*) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/Qt5/lib/libQt5Core.so.5
#30 0x00007ffff1f63412 in QApplicationPrivate::notify_helper(QObject*, QEvent*) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/Qt5/lib/libQt5Widgets.so.5
#31 0x00007ffff1f6a1f8 in QApplication::notify(QObject*, QEvent*) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/Qt5/lib/libQt5Widgets.so.5
#32 0x00007ffff2c1e0d6 in sipQApplication::notify(QObject*, QEvent*) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/QtWidgets.abi3.so
#33 0x00007ffff669d808 in QCoreApplication::notifyInternal2(QObject*, QEvent*) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/Qt5/lib/libQt5Core.so.5
#34 0x00007ffff1f6953a in QApplicationPrivate::sendMouseEvent(QWidget*, QMouseEvent*, QWidget*, QWidget*, QWidget**, QPointer<QWidget>&, bool, bool) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/Qt5/lib/libQt5Widgets.so.5
#35 0x00007ffff1fbafe8 in QWidgetWindow::handleMouseEvent(QMouseEvent*) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/Qt5/lib/libQt5Widgets.so.5
#36 0x00007ffff1fbdcf3 in QWidgetWindow::event(QEvent*) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/Qt5/lib/libQt5Widgets.so.5
#37 0x00007ffff1f6343c in QApplicationPrivate::notify_helper(QObject*, QEvent*) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/Qt5/lib/libQt5Widgets.so.5
#38 0x00007ffff1f69f20 in QApplication::notify(QObject*, QEvent*) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/Qt5/lib/libQt5Widgets.so.5
#39 0x00007ffff2c1e0d6 in sipQApplication::notify(QObject*, QEvent*) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/QtWidgets.abi3.so
#40 0x00007ffff669d808 in QCoreApplication::notifyInternal2(QObject*, QEvent*) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/Qt5/lib/libQt5Core.so.5
#41 0x00007ffff316f56d in QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::MouseEvent*) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/Qt5/lib/libQt5Gui.so.5
#42 0x00007ffff3170955 in QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent*) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/Qt5/lib/libQt5Gui.so.5
#43 0x00007ffff314c8ab in QWindowSystemInterface::sendWindowSystemEvents(QFlags<QEventLoop::ProcessEventsFlag>) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/Qt5/lib/libQt5Gui.so.5
#44 0x00007ffff166569a in xcbSourceDispatch(_GSource*, int (*)(void*), void*) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/Qt5/plugins/platforms/../../lib/libQt5XcbQpa.so.5
#45 0x00007ffff60f1d3b in g_main_context_dispatch () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
#46 0x00007ffff61466c8 in ?? () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
#47 0x00007ffff60ef3e3 in g_main_context_iteration () from /lib/x86_64-linux-gnu/libglib-2.0.so.0
#48 0x00007ffff66f91cc in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/Qt5/lib/libQt5Core.so.5
#49 0x00007ffff669c21a in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/Qt5/lib/libQt5Core.so.5
#50 0x00007ffff66a51d3 in QCoreApplication::exec() () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/Qt5/lib/libQt5Core.so.5
#51 0x00007ffff2a69be1 in meth_QApplication_exec_ () from /home/qchen/pyqt5-writing/.venv/lib/python3.11/site-packages/PyQt5/QtWidgets.abi3.so
#52 0x000000000051ad90 in ?? ()
#53 0x00000000004e75dc in _PyObject_MakeTpCall ()
#54 0x00000000004fb152 in _PyEval_EvalFrameDefault ()
#55 0x000000000062e1b4 in ?? ()
#56 0x00000000004f3a67 in PyEval_EvalCode ()
#57 0x0000000000647c37 in ?? ()
#58 0x0000000000645350 in ?? ()
#59 0x0000000000650d15 in ?? ()
#60 0x0000000000650a64 in _PyRun_SimpleFileObject ()
#61 0x0000000000650833 in _PyRun_AnyFileObject ()
#62 0x000000000064f787 in Py_RunMain ()
#63 0x000000000061ee0d in Py_BytesMain ()
#64 0x00007ffff7c6cd90 in __libc_start_call_main (main=main@entry=0x61ed60, argc=argc@entry=2, argv=argv@entry=0x7fffffffdb18) at ../sysdeps/nptl/libc_start_call_main.h:58
#65 0x00007ffff7c6ce40 in __libc_start_main_impl (main=0x61ed60, argc=2, argv=0x7fffffffdb18, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdb08) at ../csu/libc-start.c:392
#66 0x000000000061ec95 in _start ()

对应程序里load_item函数中的self.editor.setPlainText(read_file(fn))这一句调用。这句调用跟构造函数中的self.editor.setPlainText(read_file("segfaults_pyqt5.py"))第一次载入文件完全相同。但第一次是成功的。

问题可能在read_file函数吗?

def read_file(filename) -> str:
    return open(filename, 'r', encoding="utf-8").read()

这个函数经过测试,也没有问题。setPlainText应该也是没有问题的,这么大的bug不可能还会留给我。毕竟大家都有Qt5的源代码。

那么就是两者时间的关系上面。

open & read

  1. 这两个非常基础的函数调用,跟setPlainText会发生什么关联呢?
  2. 程序有三个线程,这与问题有什么关联呢?
  3. 这两个调用有什么不同?

这三个问题直接导向线程安全,thread-safe。我首先怀疑,是python的open和read线程安全问题。

阅读python的文档可以看到,open函数的签名如下:

open(file, mode='r', buffering=- 1, encoding=None, errors=None, newline=None, closefd=True, opener=None)

缓冲参数提到了io.TextIOWrapper

buffering is an optional integer used to set the buffering policy. Pass 0 to switch buffering off (only allowed in binary mode), 1 to select line buffering (only usable in text mode), and an integer > 1 to indicate the size in bytes of a fixed-size chunk buffer. Note that specifying a buffer size this way applies for binary buffered I/O, but TextIOWrapper (i.e., files opened with mode=‘r+’) would have another buffering. To disable buffering in TextIOWrapper, consider using the write_through flag for io.TextIOWrapper.reconfigure(). When no buffering argument is given, the default buffering policy works as follows:
缓冲是一个可选的整数,用于设置缓冲策略。传递0以关闭缓冲(仅允许在二进制模式下),传递1以选择行缓冲(仅在文本模式下可用),传递大于1的整数以指示固定大小的块缓冲区的大小(以字节为单位)。请注意,以这种方式指定缓冲区大小仅适用于二进制缓冲I/O,但是TextIOWrapper(即,使用mode ='r +'打开的文件)将具有另一个缓冲区。要在TextIOWrapper中禁用缓冲,请考虑使用io.TextIOWrapper.reconfigure()的write_through标志。

再跟着追踪到io.TextIOWrapper的文档,可以看到:

TextIOWrapper objects are not thread-safe.

这就是问题的关键所在了。open和read都是线程不安全的。在PyQt5的事件循环中,read的缓冲数据区可能被其他线程修改,导致setPlainText的时候出现问题。

PyQt5的实现(包括setPlainText)应该是线程安全的, 那么我们就把这个部分更换为PyQt5的形式。

QFile

重写read_file函数:

def read_file(filename) -> str:
    fid = QFile(filename)
    if not fid.open(QIODevice.ReadOnly):
        raise IOError
    file_content = str(fid.readAll(), encoding='utf-8')
    fid.close()
    return file_content

这样就可以了。

总结

  1. python的open和read函数不是线程安全的。
  2. PyQt5中应该使用QFile代替open和read函数。

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

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

相关文章

怎么恢复回收站?分享4个宝藏方法!

案例&#xff1a;怎么恢复回收站 【请问大家怎么恢复误删的文件呀&#xff1f;如果回收站被清空了&#xff0c;又应该怎么恢复呢&#xff1f;】 电脑回收站是我们存储被删除文件的地方。但是有时候&#xff0c;我们会不小心把一些重要的文件或者照片误删了。这时候&#xff0…

LED驱动程序框架

1. 字符设备驱动程序框架 2. 基于分层思想的LED驱动 2.1 把驱动拆分为通用的框架和具体的硬件操作 把驱动拆分为通用的框架(leddrv.c)、具体的硬件操作(board_X.c)&#xff1a; 如图&#xff1a; 以面向对象的思想&#xff0c;改进代码 抽象出一个结构体&#xff1a; 每个…

数据库管理-第七十三期 最近(20230509)

数据库管理 2023-05-09 第七十三期 最近1 证书2 EMCC 13.5.0.143 破百总结 第七十三期 最近 五一前后&#xff0c;除了X8那台的故障以外&#xff0c;还是做了或者探索了一些其他的东西。 1 证书 在五一假期的最后一天&#xff0c;还是在家通过线上的方式通过了1Z0-902&#…

Springboot +Flowable,三种常见网关的使用(排他、并行、包容网关)(一)

一.简介 Flowable 中常用的网关主要有三种类型&#xff0c;分别是&#xff1a; 排他网关并行网关包容网关 下面来说下这三种的网关的概念和用法。 二.排他网关 排他网关&#xff0c;也叫互斥网关&#xff0c;截图如下&#xff1a; 排他网关有一个入口&#xff0c;多个有效…

亚马逊云科技发力医疗与生命科学行业,加速数字化创新

2023年4月27日&#xff0c;亚马逊云科技医疗与生命科学行业峰会召开&#xff0c;会议汇聚了业界专家和思想领袖&#xff0c;共同探讨行业数字化转型和创新之道。作为全球医疗及生命科学行业云计算引领者&#xff0c;亚马逊云科技将围绕数据、算力和行业用户体验三大需求发力&am…

MySQL百万数据深度分页优化思路分析

业务场景 一般在项目开发中会有很多的统计数据需要进行上报分析&#xff0c;一般在分析过后会在后台展示出来给运营和产品进行分页查看&#xff0c;最常见的一种就是根据日期进行筛选。这种统计数据随着时间的推移数据量会慢慢的变大&#xff0c;达到百万、千万条数据只是时间…

UWA发布 | Unity手游性能蓝皮书

最新2022年度Unity手游蓝皮书出炉&#xff01;此次发布分析了2022年1月至2023年3月期间&#xff0c;游戏行业使用Unity引擎进行手游开发过程中及游戏上线后的性能表现&#xff0c;从测试机型分布、引擎各模块开销、内存占用等方面进行汇总分析&#xff0c;反映了Unity手游行业的…

Flink dataStream,如何开窗,如何进行窗口内计算

目录 开窗方式 windowAll() window() 窗口类型 基于时间 基于数量 开窗后的处理函数 全量聚合函数&#xff08;也叫窗口函数&#xff09; 增量聚合函数 增量聚合函数携带一个全量聚合函数 开窗方式 windowAll() 对于没有keyBy的数据流 window() 对于KeyBy后的数据…

交工技术文档表格-SH3503-2001

(阀门试验记录)(管道补偿器安装记录)(管道组成件校验性检查记录)(SHA级管道管螺纹、密封面加工记录)(高压、SHA级管道弯管加工记录)(管道静电接地测试记录)管道系统安装检查与压力试验记录)管道系统泄露性与真空试验记录)(管道吹洗、清洗脱脂记录)(给排水压力管道强度及严密试验…

685页40万字某省市场监管智慧应用一体化项目(word可编辑)

1.2.3.1 数字XX公共能力建设现状 1.2.3.1.1 数字XX通用基础应用平台现状 通用基础应用平台提供具有共性特征的跨部门、跨层级业务应用&#xff0c;与本项目有关的平台包括某省网上办事大厅、某省政务服务 APP 统一平台&#xff08;X政通 APP&#xff09;、某省公共信用信息平…

一次SQL的完整处理流程

流程&#xff1a; 1. 客户端到连接器是通过TCP/IP SSL通信协议连接的&#xff0c; 2.连接器验证MySQL权限信息是否正常&#xff0c;连接量是否正常&#xff0c;长时间没有连接服务器会自动断开等等 &#xff1b; 3. 然后到解析器&#xff0c;通过客户端发过来的sql进行语法解析…

电力系统储能调峰、调频模型研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

FS2114恒流模式的PWM升压IC,内置过温、关断、欠压、过流保护

FS2114是升压DC-DC转换器。其内置0.2Ω功率MOSFET的PWM电路&#xff0c;使该稳压器具有高效率。内部补偿网络还可以程度地 减少了6个外部元件的数量。 0.6V精密基准电压&#xff0c;内部软启动功能可以减低浪涌电流。 FS2114采用SOT23-6L封装&#xff0c;为应用节省空 间PCB。…

手机录屏怎么操作?有哪些好用的方法

在现代科技的时代&#xff0c;手机录屏已经成为了常见的操作。这项技术允许我们在手机上录制视频并分享给他人。但是&#xff0c;很多人可能并不知道如何进行手机录屏。下面我们将介绍手机录屏的操作方法和一些值得推荐的工具。 手机录屏操作方法 对于iOS用户&#xff0c;可以…

Java设计模式-适配器模式

适配器模式&#xff08;Adapter Pattern&#xff09;是一种常见的设计模式&#xff0c;它主要用于在不改变现有系统结构的情况下&#xff0c;将一个类的接口转换成客户端所期望的另一个接口。在本文中&#xff0c;我们将介绍适配器模式的基本概念、实现方法以及优缺点&#xff…

FS4056是一款完整的单节锂离子电池采用恒定电流/恒定电压线性充电器IC

FS4056是一款完整的单节锂离子电池采用恒定电流/恒定电压线性充电器IC。其底部带有散热片的 ESOP8/DIP8 封装与较少的外部元件数目使得 FS4056成为便携式应用的理想选择。FS4056可以适合 USB 电源和适配器电源工作。   由于采用了内部 PMOSFET 架构&#xff0c;加上防倒充电路…

CM201-1-YS_当贝纯净桌面-线刷固件包

CM201-1-YS_当贝纯净桌面-线刷固件包-内有教程 特点&#xff1a; 1、适用于对应型号的电视盒子刷机&#xff1b; 2、开放原厂固件屏蔽的市场安装和u盘安装apk&#xff1b; 3、修改dns&#xff0c;三网通用&#xff1b; 4、大量精简内置的没用的软件&#xff0c;运行速度提…

连接型CRM助力医疗企业把“成本中心”变成“利润中心”

在市场竞争日益加剧的情形下&#xff0c;企业获客成本大幅上涨&#xff0c;存量客户的维护和开发开始被重视&#xff0c;售后服务部门的职责在企业中发挥的价值越来越大。因为企业售后服务不仅能帮助客户解决问题的部门&#xff0c;还是客户与企业沟通的桥梁&#xff0c;将客户…

Linux——进程间通信(System V共享内存)

目录 共享内存示意图(理解共享内存的关键) shmget函数 第一个参数 ftok函数 使用ftok打印key值 第二个参数 第三个参数 返回值 打印shmget 再谈key值 举例理解key值 共享内存的过程 创建共享内存 关联共享内存 去除关联 ​编辑 使用共享内存通信 删除共享内存…

关于财会〔2020〕6 号文件解读,本地化部署+区块链或成为新趋势?

2020年3月&#xff0c;财政部、国家档案局发布了《关于规范电子会计凭证报销入账归档的通知》财会〔2020〕6 号&#xff08;以下简称“通知”&#xff09;&#xff0c;对于电子会计凭证的范围、法律效力、适用条件都做出了进一步规范。这可以看作是财会档案管理由纸质化迈向全面…